leetcode
leetcode 1351 ~ 1400
和为奇数的子数组数目

和为奇数的子数组数目

难度:

标签:

题目描述

代码结果

运行时间: 86 ms, 内存: 20.5 MB


/*
题目思路:
利用Java Stream的方式来计算和为奇数的子数组数目。思路类似于普通的Java解法,通过计算前缀和的奇偶性来判断子数组的和是否为奇数。
*/

import java.util.stream.IntStream;

public class OddSumSubarraysStream {
    public static int countOddSumSubarrays(int[] arr) {
        final int MOD = 1000000007;
        int oddCount = 0, evenCount = 1, result = 0, prefixSum = 0;
        for (int num : arr) {
            prefixSum += num;
            if (prefixSum % 2 == 0) {
                result = (result + oddCount) % MOD;
                evenCount++;
            } else {
                result = (result + evenCount) % MOD;
                oddCount++;
            }
        }
        return result;
    }

    public static void main(String[] args) {
        int[] arr = {1, 3, 5};
        System.out.println(countOddSumSubarrays(arr)); // 输出:4
    }
}

解释

方法:

这个题解利用了数组的前缀和来确定子数组的和是否为奇数。通过维护两个计数器:odd(记录以当前元素结尾的数组的前缀和为奇数的情况数)和even(记录为偶数的情况数,初始为1代表空子数组的前缀和为0,是偶数)。遍历数组,更新当前的前缀和。如果前缀和为奇数,则增加odd计数;如果为偶数,则增加even计数。最终,奇数前缀和的数量乘以偶数前缀和的数量就是结果,因为每个奇数前缀和都可以与之前的任意一个偶数前缀和组合形成一个和为奇数的子数组。

时间复杂度:

O(n)

空间复杂度:

O(1)

代码细节讲解

🦆
为什么在这个算法中需要初始化偶数前缀和的计数器为1?
在算法中初始化偶数前缀和计数器为1是因为考虑了一个初始的空数组情况,其前缀和为0,而0是一个偶数。这样的初始化有助于处理数组中第一个元素就是奇数的情况,确保能正确统计从数组开始到当前位置的子数组的和。如果不将其初始化为1,那么在数组第一个元素为奇数时,将无法计入以它为结束点的子数组。
🦆
请解释如果当前的前缀和是奇数,为什么累加odd计数器?
当当前的前缀和是奇数时,累加odd计数器的原因在于记录下有多少个以当前元素结尾的前缀和是奇数。这是因为任何从数组开始到当前元素的子段(前缀)如果其和是奇数,那么这个前缀本身就可以被视为一个和为奇数的子数组。此外,这些奇数前缀和在后续的计算中,当再次遇到奇数前缀和时,可以与之前记录的偶数前缀和组合,形成新的和为奇数的子数组。
🦆
算法中提到,最终结果是奇数前缀和与偶数前缀和的组合数。这种组合是如何形成子数组和为奇数的?
这种组合形成子数组和为奇数的原理基于数学性质:两个奇数相加得到偶数,两个偶数相加得到偶数,但一个奇数和一个偶数相加得到奇数。在解题方法中,通过计算所有奇数前缀和与偶数前缀和之间的组合,可以找到所有可能的子数组,这些子数组的和为奇数。这是因为每个奇数前缀和都可以与之前的任意一个偶数前缀和相减(即两个前缀和之间的元素组成的子数组),结果是奇数。
🦆
为什么结果需要对10^9 + 7取余?这个数字有什么特别的意义吗?
在许多编程竞赛和算法实现中,需要对结果取模以防止数字溢出并保持结果的大小在可管理的范围内。10^9 + 7是一个常用的大质数,使用它作为模数有几个好处:它足够大,可以避免常见计算中的溢出;同时作为质数,它在数学运算中,尤其是在需要使用逆元时,具有良好的数学性质。使用质数作为模数可以避免一些在模运算中可能出现的问题。

相关问题