作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/count-number-of-nice-subarrays/solution/tong-ji-you-mei-zi-shu-zu-by-leetcode-solution/
统计「优美子数组」
题目描述
给定一个整数数组 nums 和一个整数 k。
如果某个连续子数组中恰好有 k 个奇数数字,就认为这个子数组是「优美子数组」。
请返回这个数组中「优美子数组」的数目。

题解
- 单独建立一个 odd\textit{odd}odd 数组来记录第 i 个奇数的下标。那么我们可以枚举奇数,假设当前枚举到第 i 个,那么 [odd[i],odd[i+k−1]][\textit{odd}[i],\textit{odd}[i+k-1]][odd[i],odd[i+k−1]] 这个子数组就恰好包含 k 个奇数。
- 由于奇数和奇数间存在偶数,所以一定存在其他子数组 [l,r][l,r][l,r] 满足 [l,r][l,r][l,r] 包含 [odd[i],odd[i+k−1]][\textit{odd}[i],\textit{odd}[i+k-1]][odd[i],odd[i+k−1]]且 [l,r][l,r][l,r]里的奇数个数为 k 个。
- 因此对于第 i 个奇数,符合条件的 [l,r][l,r][l,r] 的个数:
(odd[i]−odd[i−1])∗(odd[i+k]−odd[i+k−1])(\textit{odd}[i] - \textit{odd}[i - 1]) * (\textit{odd}[i + k] - \textit{odd}[i + k - 1])(odd[i]−odd[i−1])∗(odd[i+k]−odd[i+k−1])
只需要遍历一遍 odd\textit{odd}odd 数组即可求得最后的答案,注意边界的处理。
复杂度分析
时间复杂度:O(n)O(n)O(n),其中 n 为数组的大小。遍历 odd\textit{odd}odd 数组最坏情况下需要 O(n)O(n)O(n)的时间。
空间复杂度:O(n)O(n)O(n),其中 n 为数组的大小。odd\textit{odd}odd 数组需要 O(n)O(n)O(n) 的空间。
方法二:前缀和 + 差分


复杂度分析
时间复杂度:O(n)O(n)O(n),其中 n 为数组的大小。
空间复杂度:O(n)O(n)O(n),其中 n 为数组的大小。频次数组 cnt\textit{cnt}cnt 记录的最大值不会超过 n ,因此只需要额外的 O(n)O(n)O(n) 的空间
代码
class Solution {
public:
int numberOfSubarrays(vector<int>& nums, int k) {
/*
// 方法一:建立odd数组记录奇数所在位置的下标
int n = nums.size();
int odd[n+2], ans = 0, index = 0;
for(int i = 0; i < n; i++){
if(nums[i] & 1)
odd[++index] = i;
}
odd[0] = -1;
odd[++index] = n;
for(int i = 1; i + k <= index; i++)
ans += (odd[i]-odd[i-1])*(odd[i+k]-odd[i+k-1]);
return ans;
*/
// 方法二:前缀和+差分
int n = nums.size();
cnt.resize(n + 1, 0);
int odd = 0, ans = 0;
cnt[0] = 1;
for(int i = 0; i < n; i++){
odd += nums[i] & 1;
ans += odd >= k ? cnt[odd - k] : 0;
cnt[odd] += 1;
}
return ans;
}
private:
vector<int> cnt;
};
小结
数组:int a[n]; 一种内置的数据类型。数组是存放类型相同的对象的容器,数组的大小确定不变,不能随意向数组中增加元素。
向量:vector a;类型相同的对象的容器,vector的大小可以变化,可以向数组中增加元素。
本文介绍了一种统计数组中优美子数组数量的方法,优美子数组定义为恰好包含k个奇数的连续子数组。通过建立奇数下标数组或使用前缀和与差分技巧,可以在O(n)的时间复杂度内解决问题。
533

被折叠的 条评论
为什么被折叠?



