作者: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的大小可以变化,可以向数组中增加元素。