LeetCode992. K 个不同整数的子数组

189 阅读1分钟

K 个不同整数的子数组

题目描述

992. K 个不同整数的子数组

难度困难415收藏分享切换为英文接收动态反馈

给定一个正整数数组 nums和一个整数 k ,返回 num 中 「好子数组」 的数目。

如果 nums 的某个子数组中不同整数的个数恰好为 k,则称 nums 的这个连续、不一定不同的子数组为 「****好子数组 」

  • 例如,[1,2,3,1,2] 中有 3 个不同的整数:12,以及 3

子数组 是数组的 连续 部分。

示例 1:

输入:nums = [1,2,1,2,3], k = 2
输出:7
解释:恰好由 2 个不同整数组成的子数组:[1,2], [2,1], [1,2], [2,3], [1,2,1], [2,1,2], [1,2,1,2].

示例 2:

输入:nums = [1,2,1,3,4], k = 3
输出:3
解释:恰好由 3 个不同整数组成的子数组:[1,2,1,3], [2,1,3], [1,3,4].

提示:

  • 1 <= nums.length <= 2 * 104
  • 1 <= nums[i], k <= nums.length

题解

可以用双指针去枚举一个端点,假设枚举右端点,当右端点 ii 确定后,考虑左端点有多少种不同的选择。

假设 j1j1 是满足区间 [j1,i][j1, i] 中恰好有 kk 个不同元素最靠左的位置,再往左就不满足这个条件了, j2j2 是满足区间 [j2,i][j2, i] 中恰好有 k1k - 1 个不同元素最靠左的位置,那么满足条件的左端点选法可以在区间 [j1,j21][j1, j2 - 1] 中选,也就是 j2j1j2 - j1 种。

j1j1 是一个上界,j2j2 是一个下界。

image-20221204202720124

我们可以发现,当 ii 单调向右走时,jj 也一定随着 ii 单调向右走,也就是 jjii 的一个单调函数,因此我们可以使用双指针算法。

反证法证明单调性:

定义 jj 是最靠左并且满足条件的位置,

假设当 ii 往右走到 ii'jj 往左走到 jj',满足区间 [j,i][j',i'] 中恰好有 kk 个不同的元素, 那么区间 [j,i][j',i] 中一定最多有 kk 个不同的元素,因为区间更小了,此时我们可以发现 jj 的位置与我们定义的矛盾,因为jj 可以再往左移动到 jj'。与假设矛盾,所以当 ii 往右走时,jj 一定不会往左走。

image-20221204203752548

代码

class Solution {
public:
    int subarraysWithKDistinct(vector<int>& nums, int k) {
        int res = 0;
        // 要统计数的种类而且要能表示重复的数 所以用map
        unordered_map<int, int> S1, S2;
        int n = nums.size();
        for (int i = 0, j1 = 0, j2 = 0; i < n; i++) {
            S1[nums[i]]++;
            while (S1.size() > k) {
                S1[nums[j1]]--;
                // 如果出现次数为0 则删除 就不用额外用一个变量记录数的种类 size就可以代表种类
                if (!S1[nums[j1]]) S1.erase(nums[j1]);
                j1++;
            }

            S2[nums[i]]++;
            while (S2.size() > k - 1) {
                S2[nums[j2]]--;
                if (!S2[nums[j2]]) S2.erase(nums[j2]);
                j2++;
            }

            res += j2 - j1;
        }
        return res;
    }
};