K 个不同整数的子数组
题目描述
难度困难415收藏分享切换为英文接收动态反馈
给定一个正整数数组 nums和一个整数 k ,返回 num 中 「好子数组」 的数目。
如果 nums 的某个子数组中不同整数的个数恰好为 k,则称 nums 的这个连续、不一定不同的子数组为 「****好子数组 」。
- 例如,
[1,2,3,1,2]中有3个不同的整数:1,2,以及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 * 1041 <= nums[i], k <= nums.length
题解
可以用双指针去枚举一个端点,假设枚举右端点,当右端点 确定后,考虑左端点有多少种不同的选择。
假设 是满足区间 中恰好有 个不同元素最靠左的位置,再往左就不满足这个条件了, 是满足区间 中恰好有 个不同元素最靠左的位置,那么满足条件的左端点选法可以在区间 中选,也就是 种。
是一个上界, 是一个下界。
我们可以发现,当 单调向右走时, 也一定随着 单调向右走,也就是 是 的一个单调函数,因此我们可以使用双指针算法。
反证法证明单调性:
定义 是最靠左并且满足条件的位置,
假设当 往右走到 , 往左走到 ,满足区间 中恰好有 个不同的元素, 那么区间 中一定最多有 个不同的元素,因为区间更小了,此时我们可以发现 的位置与我们定义的矛盾,因为 可以再往左移动到 。与假设矛盾,所以当 往右走时, 一定不会往左走。
代码
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;
}
};