问题描述
给定一个正整数数组 A,如果 A 的某个子数组中不同整数的个数恰好为 K,则称 A 的这个连续、不一定独立的子数组为好子数组。
(例如,[1,2,3,1,2] 中有 3 个不同的整数:1,2,以及 3。)
返回 A 中好子数组的数目。
示例 1:
输入:A = [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].
解题报告
考虑累加以每个数字结尾的好子数组的个数。
那么如何计算以每个数字结尾的子数组的个数呢?
假设 [r,i]
是以 A[i]
结尾的好数组的最小区间,[l,i]
是以 A[i]
结尾的好数组的最大区间,
则每个累加 r-l+1
即可。
所以现在问题变成了 如何计算 l
和 r
。
l
意味着 [l,i]
的区间中不同整数的个数取不到 K+1
时,不开始更新 l
;
r
意味着 [r,i]
的区间中不同整数的个数刚取到 K
时,开始更新 r
。
想法其实挺简单的,但是编码实现时,总是不知如何下手,哎,还是要多多练习吧
这题和 Leetcode 1248. 统计「优美子数组」 的【滑动窗口解法】有着异曲同工之妙,只是 Leetcode 1248
中,数组中只有两类数,奇数&偶数,所以我们不需要令设两个数组来记录窗口内的奇数和偶数。
实现代码
代码部分完全源自参考的文献,侵权必删
class Solution{
public:
int subarraysWithKDistinct(vector<int>&A, int K){
int n=A.size();
vector<int>cl(n+1, 0), cr(n+1, 0);
int l=0, r=0, ans=0, nl=0, nr=0;
for(int i=0;i<n;i++){
if(cl[A[i]]++==0) nl++;
while(nl>K){
if(--cl[A[l++]]==0) nl--;
}
if(cr[A[i]]++==0) nr++;
while(nr>=K){
if(--cr[A[r++]]==0) nr--;
}
ans+=(r-l);
}
return ans;
}
};
参考资料
[1] Leetcode 992. K 个不同整数的子数组
[2] 每日算法系列【LeetCode 992】K个不同整数的子数组
[3] Leetcode 1248. 统计「优美子数组」
[4] Leetcode 1248. 统计「优美子数组」【记录奇数位置&滑动窗口&前缀和】