好的!让我们详细解释 LeetCode 1524. 和为奇数的子数组数目 这道题的思路和解法。
题目 : https://leetcode.cn/problems/number-of-sub-arrays-with-odd-sum/description/
题目分析
问题描述 :
给定一个整数数组 arr
,返回其中和为奇数的子数组的数目。由于答案可能很大,结果需对 10^9 + 7
取余。
示例 :
输入:arr = [1, 3, 5]
输出:4
解释:
- 子数组
[1]
、[3]
、[5]
的和均为奇数。 - 子数组
[1, 3, 5]
的和为1 + 3 + 5 = 9
,也是奇数。
暴力解法(超时)
思路 :
枚举所有子数组,计算每个子数组的和并判断奇偶性。
代码:
cpp
class Solution {
public:
int numOfSubarrays(vector<int>& arr) {
const int MOD = 1e9 + 7;
int count = 0;
int n = arr.size();
for (int i = 0; i < n; i++) {
int sum = 0;
for (int j = i; j < n; j++) {
sum += arr[j];
if (sum % 2 == 1) {
count = (count + 1) % MOD;
}
}
}
return count;
}
};
复杂度:
- 时间复杂度:O(n²)
- 空间复杂度:O(1)
优化解法:前缀和 + 奇偶计数
核心思路:
- 前缀和的奇偶性 :子数组
[i, j]
的和为prefix[j+1] - prefix[i]
,其中prefix[k]
表示前k
个元素的和。 - 奇偶性规律 :若
prefix[j+1]
和prefix[i]
的奇偶性不同,则子数组和为奇数。 - 哈希表统计:动态维护前缀和为奇数和偶数的次数,遍历数组时累加符合条件的子数组数目。
代码:
cpp
class Solution {
public:
int numOfSubarrays(vector<int>& arr) {
const int MOD = 1e9 + 7;
int count = 0;
int prefix = 0; // 当前前缀和
int odd = 0; // 前缀和为奇数的次数
int even = 1; // 前缀和为偶数的次数(初始包含前缀和为0的情况)
for (int num : arr) {
prefix += num;
if (prefix % 2 == 0) {
// 当前前缀和为偶数,加上之前前缀和为奇数的次数
count = (count + odd) % MOD;
even++;
} else {
// 当前前缀和为奇数,加上之前前缀和为偶数的次数
count = (count + even) % MOD;
odd++;
}
}
return count;
}
};
复杂度:
- 时间复杂度:O(n)
- 空间复杂度:O(1)
详细解释
-
前缀和的奇偶性 :
对于子数组
[i, j]
,其和为prefix[j+1] - prefix[i]
。- 若
prefix[j+1]
为偶数,prefix[i]
为奇数,则和为奇数。 - 若
prefix[j+1]
为奇数,prefix[i]
为偶数,则和为奇数。
- 若
-
动态维护奇偶次数:
odd
:记录遍历到当前位置时,前缀和为奇数的次数。even
:记录遍历到当前位置时,前缀和为偶数的次数。- 初始值 :
even = 1
,因为空数组的前缀和为 0(偶数)。
-
累加结果:
- 若当前前缀和为偶数,则它可以与之前所有奇数前缀和形成有效子数组,累加
odd
。 - 若当前前缀和为奇数,则它可以与之前所有偶数前缀和形成有效子数组,累加
even
。
- 若当前前缀和为偶数,则它可以与之前所有奇数前缀和形成有效子数组,累加
示例验证
输入:arr = [1, 3, 5]
步骤如下:
- 初始状态 :
prefix = 0
,odd = 0
,even = 1
,count = 0
。 - 处理 1 :
prefix = 1
(奇数),count += even = 1
,odd = 1
,even = 1
。 - 处理 3 :
prefix = 4
(偶数),count += odd = 1 + 1 = 2
,odd = 1
,even = 2
。 - 处理 5 :
prefix = 9
(奇数),count += even = 2 + 2 = 4
,odd = 2
,even = 2
。
最终结果:4
,与预期一致。
总结
通过前缀和的奇偶性分析和动态计数,我们将时间复杂度从 O(n²) 优化到 O(n),空间复杂度为 O(1)。这种方法适用于所有类似的"子数组和满足某种奇偶性条件"的问题,核心在于利用前缀和的奇偶性快速查找配对。