leetcode1248. 统计「优美子数组」

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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/count-number-of-nice-subarrays
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

基本思想

滑动窗口

  • 从头到尾遍历数组,找到满足要求的最小子串
  • 统计子串两端连续偶数的个数,例如右边 r 个,左边 l 个,那么由当前最小子串构成的优美子数组有 (r + 1) x (l + 1) 个
class Solution {
public:
    int numberOfSubarrays(vector<int>& nums, int k) {
        //找到一个优美子数组向两端扩展,统计个数,用乘法的形式左面三个右面三个4x4
        //然后再从第二个奇数所构成的优美数组向两端扩展
        
        if(nums.size() == 0 || k > nums.size())
            return 0;
        
        int res = 0;
        deque<int> pos;
        int s = 0;//存储上一次最小优美子数组的最左面元素的下标的下一个位置,便于统计最小优美子数组的左面有几个偶数
        for(int i = 0; i < nums.size(); ++i){
            if(nums[i] & 1){
                pos.push_back(i);//保存奇数的下标
            }
            if(pos.size() == k){
                int l = pos.front() - s;//左面偶数的个数
                int r = 0;
                int k = i + 1;
                //统计右面连续偶数的个数
                while(k < nums.size() && ((nums[k] & 1) == 0)){
                    ++r; 
                    ++k;
                }
                //cout << i << " " <<  l << " " << r << endl;
                res += (l + 1) * (r + 1);
                if(k == nums.size())
                    break;
                s = pos.front() + 1;
                pos.pop_front();
            }
        }
        return res;
    }
};

前缀和

  • 统计以当前元素结尾的序列中优美子数组的个数
  • 遍历数组的过程中,统计前 i 个数字中奇数的个数
  • 如果奇数的个数 odd大于等于 k,需要统计前 i 个数字中有多少个奇数个数为odd - k的子序列,有几个就说明能构成几个优美子数组(这里不太好理解,参考:官方题解)
class Solution {
public:
    int numberOfSubarrays(vector<int>& nums, int k) {
        vector<int> cnt(nums.size() + 1, 0);
        cnt[0] = 1;
        int odd = 0;//统计前i个数中奇数的个数
        int res = 0;
        for(int i = 0; i < nums.size(); ++i){
            odd += (nums[i] & 1);
            res += ((odd >= k ? cnt[odd - k] : 0));
            cnt[odd]++;
        }
        return res;
    }
};

另一种写法:

  • 用map统计前 i 个字符中奇数的个数,个数作为键,有几个这样的子序列作为值
  • 遍历的过程中,统计目前为止奇数的个数,并借助map查找前缀中有几个序列中有sum - k个奇数
class Solution {
public:
    int numberOfSubarrays(vector<int>& nums, int k) {
        unordered_map<int, int> m;
        int sum = 0;
        m[0] = 1;
        int cnt = 0;
        for(int i = 0; i < nums.size(); ++i){
            if(nums[i] % 2)
                ++sum;
            if(m.count(sum - k))
                cnt += m[sum - k];
            ++m[sum];
        }
        return cnt;
    }
};
已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页