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个奇数,且数组的左右两端都是奇数,比如数组1,2,2,1,1,k=3,这样的数组中,肯定只有1个子数组满足题意条件。即数组本身。

现在考虑在数组两端添加一些偶数,比如2,1,2,2,1,1,2,k=3。这样的数组可以有4个子数组满足题意。即分别加上左边的0,1个偶数组成子数组,加上右边的0,1个偶数组成子数组。

观察可以得知,在这种条件下,数组中优美子数组的个数为(左边偶数个数+1)*(右边偶数个数+1)

考虑更一般的情况,我们可以记录一下数组中每个奇数的缝隙直接插了多少个偶数。设数组中奇数总个数为n,则有数组even[],even[0]表示数组最左端的偶数个数,even[1]表示第一个奇数和第二个奇数之间的偶数个数,even[n]表示数组最后一个奇数右端的偶数个数。则第i个奇数左端有even[i-1]个偶数,第i+k个奇数右端有even[k]偶数。循环遍历even数组即得答案。

点击查看代码
class Solution {
public:
    int numberOfSubarrays(vector<int>& nums, int k) {
        vector<int> even;
        int evenCount = 0, res = 0;
        for(int i = 0; i < nums.size(); i++)
        {
            if(nums[i] % 2)
            {
                even.push_back(evenCount);
                evenCount = 0;
                if(i == nums.size() - 1) even.push_back(evenCount);
            }
            else if(i == nums.size() - 1){
                evenCount++;
                even.push_back(evenCount);
            }
            else    
                evenCount++;
                
        }

        for(int i = 0; i + k < even.size(); i++)
        res += (even[i] + 1) * (even[i + k] + 1);
        
        return res;
    }
};

 

解法二:

这是leetcode官方的解法,用到前缀和的思想。官方解释很难懂,我的理解是,维护一个变量odd,存放到目前位置i为止数组中出现奇数的个数。开一个cnt[]数组,记录数组中奇数个数为odd的次数。比如,现在遍历到nums[i],到i位置的奇数个数为odd,那么我们就让odd+1。i这个位置为终点时有多少子数组满足优美的条件呢?应该是cnt[odd - k](注意检查odd - k>=0)。

点击查看代码
class Solution {
public:
    int numberOfSubarrays(vector<int>& nums, int k) {
        vector<int> cnt(nums.size() + 1, 0);
        int odd = 0, res = 0;
        cnt[0] = 1;
        for(int i = 0; i < nums.size(); i++)
        {
            odd += (nums[i] & 1); 
            res += (odd - k >= 0) ? cnt[odd - k] : 0;
            cnt[odd] += 1;
        }
        return res;
    }
};

两种解法时间复杂度都为o(n)。空间复杂度都为o(n)。下图中提交时间较早的是解法一。

posted @ 2021-10-24 20:10  柚子z  阅读(62)  评论(0)    收藏  举报