给你一个整数数组 nums 和一个整数 k。
如果某个 连续 子数组中恰好有 k 个奇数数字,我们就认为这个子数组是「优美子数组」。
请返回这个数组中「优美子数组」的数目。
示例 1:
输入:nums = [1,1,2,1,1], k = 3
输出:2
解释:包含 3 个奇数的子数组是 [1,1,2,1] 和 [1,2,1,1] 。
示例 2:
输入:nums = [2,4,6], k = 1
输出:0
解释:数列中不包含任何奇数,所以不存在优美子数组。
示例 3:
输入:nums = [2,2,2,1,2,2,1,2,2,2], k = 2
输出:16
提示:
1 <= nums.length <= 50000
1 <= nums[i] <= 10^5
1 <= k <= nums.length
分析:
这个题我是用双指针做的,也就是所谓的滑动窗口,在这个窗口中保持k个奇数,然后查找窗口左右两边偶数的个数,计算数组的个数。
在这里分享一下我看到的题解中所说的“前缀和”的方法。这里设置一个前缀和(即到当前元素为止,子数组中奇数的个数)数组arr,遍历原数组,每次遍历,记录当前的前缀和。那么该题的答案就是双重循环arr[j] - arr[i] == k(j > i)的个数。但是对于该题而言是TLE的,所以我们需要优化一下。
我们可以这样优化,利用HashMap,key:前缀和的大小,value:前缀和对应的数组的个数。不过这里前缀和的大小是依次累加的,我们可以用prefix[]这样一个数组来记录。eg.prefix[3],表示前缀和为3的数组的个数;也就是有这么多个数组,其中奇数的个数为3。
上面说了,我们需要找的是arr[j] - arr[i] == k的个数,变换一下也就是arr[j] - k == arr[i],我们找的是要满足该关系的所有数组。假设我们已经遍历到数组的位置j,对于每一个j而言,我们要找的是所有这样的数组,这些数组arr[i] == arr[j] - k,也就是求prefix[arr[i]],且arr[i] == arr[j] - k。其实也不难理解,prefix[arr[j]]表示0...j这些元素组成的数组有arr[j]个奇数,我们求arr[i] == arr[j] - k的数组个数,也就是删去前面的数组0...i,使得i...j中包含k个奇数。prefix[arr[j] - k]就是指的前面被删去的数组的个数,删去多少个,也就是剩下多少个含有k个奇数的连续子数组。
举个例子,假设到某个j的时候,arr[j] = 10,k = 7,那么此时我们求的就是prefix[3]的数量,包含3个奇数的数组有多少个,那么包含7个奇数的数组就有多少个。而这也正是我们所求的。
prefix[0] = 1是单独定义的,比如{1, 1, 1},k = 3,此时也是一个满足条件的数组,所以我们令其初值为1。
class Solution {
public:
int numberOfSubarrays(vector<int>& nums, int k) {
vector<int> prefix(nums.size() + 1, 0);
prefix[0] = 1;
int sum = 0, ans = 0;
for(int i = 0; i < nums.size(); i++){
sum += nums[i] & 1;
prefix[sum]++;
if(sum >= k) ans += prefix[sum - k];
}
return ans;
}
};