题目:
给定一个正整数数组 A,如果 A 的某个子数组中不同整数的个数恰好为 K,则称 A 的这个连续、不一定不同的子数组为好子数组。
(例如,[1,2,3,1,2] 中有 3 个不同的整数:1,2,以及 3。)
返回 A 中好子数组的数目。
例:
输入: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].
原题地址:
解题思路:
定义2个指针left和right,窗口[left, right)中的数据保存在一个map中,map[i]:代表窗口中数字i出现的次数。
- 当map.size() < K时,右移right;
- 当map.size() == K时,若right数据在map中,右移right;
- 剩余情况说明,[left, right)是一个数量最多的好子数组(再多一个则不满足),此时缩小窗口右侧至数量最小的好子数组(再少一个不满足)后再恢复至原来的[left, right)后,右移left,缩小过程中计数。
移动过程如下(蓝底为窗口区域,橙底为right):
c++代码:
class Solution {
public:
int subarraysWithKDistinct(vector<int>& A, int k) {
int left = 0;
int right = 0;
map<int, int> freq; // 记录[left,right]内数字及其频次
int total = 0;
while (right < A.size()) {
if (freq.size() < k) {
add(freq, A[right++]);
continue;
}
// freq.size() == k
map<int, int>::iterator it = freq.find(A[right]);
if (it != freq.end()) {
++it->second;
right++;
continue;
}
// freq.size() > k
// [left, right)区间内含left的子数组
int t = right - 1;
while (freq.size() == k) {
total++;
remove(freq, A[t--]);
}
while (++t < right) {
add(freq, A[t]);
}
remove(freq, A[left++]);
}
while (left < right) {
int t = right - 1;
while (freq.size() == k) {
total++;
remove(freq, A[t--]);
}
while (++t < right) {
add(freq, A[t]);
}
remove(freq, A[left++]);
}
return total;
}
void add(map<int, int>& m, int k) {
map<int, int>::iterator it = m.find(k);
if (it == m.end()) {
m[k] = 1;
} else {
++it->second;
}
}
void remove(map<int, int>& m, int k) {
map<int, int>::iterator it = m.find(k);
if (it == m.end()) return;
if (it->second == 1) {
m.erase(it);
} else {
--it->second;
}
}
};