LeetCode: 1248. 统计「优美子数组」

给你一个整数数组 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;
    }
};

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值