Leetcode 1248. 统计「优美子数组」【记录奇数位置&滑动窗口&前缀和】


本文在别人的题解【链接列于参考资料中】基础上加上些许自己的理解,若侵权立删。

问题描述

给你一个整数数组 nums 和一个整数 k。

如果某个 连续 子数组中恰好有 k 个奇数数字,我们就认为这个子数组是「优美子数组」。

请返回这个数组中「优美子数组」的数目。

示例 1:

输入:nums = [1,1,2,1,1], k = 3
输出:2
解释:包含 3 个奇数的子数组是 [1,1,2,1] 和 [1,2,1,1] 。

解题报告

记录奇数位置

我们发现,如果两个奇数之间(包含自身)一共包含了 k 个奇数,那么这 k 个奇数可以构成的连续子数组个数就是 左边连续的偶数 的个数加 1 乘以 右边连续的偶数 的个数加 1

所以 问题的关键就变成如何高效的求每个奇数前 连续的偶数 的个数

一种解决方案是记录每个奇数所在的位置,那么每个奇数前 连续的偶数 个数为当前奇数的位置减去上一个奇数的位置

另外在实现的时候需要注意一些细节问题,例如第一个奇数前 连续偶数的个数 如何求?最后一个奇数前 连续偶数的个数 如何求?

解决方案是:在最开始添加上虚拟位置 -1,在最后添加虚拟位置 n

最后遍历所有的 i, 将第 i1 作为起点,然后累加答案即可。

时间复杂度:O(n)O(n)
空间复杂度:O(n)O(n)

滑动窗口

仔细想一想,上面的思想完全可以用滑动窗口来实现,省去记录每个 1 的空间。

  • 移动窗口的右边界时,更新窗口内 1 出现的次数;
  • 当窗口内 1 的数目达到要求的 k 时,开始缩收左边界;
  • 移动左边界时,记录缩收过程遇到的 0 的个数 even,直到遇到 1
  • 累加当前的 even

时间复杂度:O(n)O(n)
空间复杂度:O(1)O(1)

前缀和

这种 做法哈希表优化系列【空间换时间】-Leetcode 560.和为 K 的子数组 有些许相似之处,都用了前缀和。不同的其中一点是:在 Leetcode 560 中,由于前缀和存在负数的可能,所以需要用哈希表来存储前缀和,便于查找,但是此题中,前缀和的范围是可控的而且为正整数,所以我们完全可以将前缀和作为 vector 的索引来存储。

同样都是前缀和,但是具体实现上,我这种菜鸡,还是挺困难的。具体想法和滑动窗口有些相似。

实现代码

记录奇数位置实现

class Solution {
public:
    int numberOfSubarrays(vector<int>& nums, int k) {
        int n = nums.size();
        vector<int> pos;
        pos.push_back(-1);
        for (int i = 0; i < n; ++i) {
            if (nums[i]&1) pos.push_back(i);
        }
        pos.push_back(n);
        int res = 0, sz = pos.size();
        for (int i = 1; i+k < sz; ++i) {
            res += (pos[i] - pos[i-1]) * (pos[i+k] - pos[i+k-1]);
        }
        return res;
    }
};

滑动窗口实现

class Solution {
public:
    int numberOfSubarrays(vector<int>& nums, int k) {
        int n = nums.size();
        int res = 0, cnt = 0, even = 0;
        int l = 0, r = 0;
        while (r < n) {
            if (cnt < k && (nums[r++]&1)) cnt++;
            if (cnt == k) {
                even = 1;
                while (!(nums[l++]&1)) even++;
                cnt--;
            }
            res += even;
        }
        return res;
    }
};

前缀和+哈希表优化实现

class Solution {
public:
    int numberOfSubarrays(vector<int>& nums, int k) {
        int n = nums.size();
        vector<int> count(n+1, 0);
        count[0] = 1;
        int res = 0, odd = 0;
        for (int i = 0; i < n; ++i) {
            odd += nums[i]&1;
            if (odd >= k) res += count[odd-k];
            count[odd]++;
        }
        return res;
    }
};

参考资料

[1] Leetcode 1248. 统计「优美子数组」
[2] 【每日算法Day 107】面试必考:良心推荐,一题三解,不看后悔一辈子

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章