Given an array A of positive integers, call a (contiguous, not necessarily distinct) subarray of A good if the number of different integers in that subarray is exactly K.
(For example, [1,2,3,1,2] has 3 different integers: 1, 2, and 3.)
Return the number of good subarrays of A.
Example 1:
Input: A = [1,2,1,2,3], K = 2
Output: 7
Explanation: Subarrays formed with exactly 2 different integers: [1,2], [2,1], [1,2], [2,3], [1,2,1], [2,1,2], [1,2,1,2].
Example 2:
Input: A = [1,2,1,3,4], K = 3
Output: 3
Explanation: Subarrays formed with exactly 3 different integers: [1,2,1,3], [2,1,3], [1,3,4].
Note:
1 <= A.length <= 20000
1 <= A[i] <= A.length
1 <= K <= A.length
Basic Idea:
拿到题目之后就会发现如果要硬算,直接求解distinct element number为 K 的 subarray 个数是比较困难的,因为这类题目一般会使用双指针或者sliding window求解,而要求所有subarray的个数就需要维持许多窗口中subarray的特征。
看了Lee215的解法之后豁然开朗,我们可以写一个function getAtMostK()来求 distinct element number <= K subarray 的个数,这样只需要返回 getAtMost(K) - getAtMost(K-1) 就可以了。
具体地,如何实现 getAtMostK() 呢?用sliding window,保证window中不同元素个数 <= K,每次右边界右移一位,而每次当前窗口中所有以right结尾的subarray的个数就是 right - left + 1,只要把每个元素结尾的符合条件的subarray个数相加,就是总的个数。
class Solution {
public int subarraysWithKDistinct(int[] A, int K) {
if (K == 0) return 0;
return getAtMostK(A, K) - getAtMostK(A, K - 1);
}
private int getAtMostK(int[] nums, int K) {
if (K == 0) return 0;
int ret = 0;
int[] count = new int[nums.length + 1];
int i = 0, j = 0, m = 0;
while (j < nums.length) {
if (count[nums[j]] == 0) {
m++;
}
count[nums[j]]++;
while (m > K) {
if (--count[nums[i]] == 0) {
m--;
}
i++;
}
// 此时保证subarray [i,j] 部分只有小于等于K个不同数字,
// 计算从i开始到j结尾的所有subarray个数
ret += j - i + 1;
j++;
}
return ret;
}
}
class Solution {
public int subarraysWithKDistinct(int[] A, int K) {
return atMostK(A, K) - atMostK(A, K - 1);
}
private int atMostK(int[] A, int K) {
int[] map = new int[A.length + 1];
int ret = 0, left = 0, count = 0;
for (int r = 0; r < A.length; ++r) {
if (map[A[r]]++ == 0) count++;
while (count > K) {
if (--map[A[left++]] == 0) count--;
}
ret += r - left + 1;
}
return ret;
}
}
class Solution {
public int subarraysWithKDistinct(int[] A, int K) {
int[] map = new int[A.length + 1];
int ret = 0, count = 0, i = 0, j = 0;
for (int curr = 0; curr < A.length; ++curr) {
// curr 为右指针,每次前进一步
if (map[A[curr]]++ == 0) count++;
while (count > K) {
if (--map[A[j++]] == 0) count--;
i = j;
// 让ij一起,因为此时j是最左边左指针的位置
}
if (count == K) {
while (map[A[j]] > 1) {
map[A[j]]--;
j++;
}
// 此时 i 指向第一个满足条件的左指针的位置
// j 指向最右边一个满足条件左指针的位置
ret += j - i + 1;
}
}
return ret;
}
}
class Solution {
public int subarraysWithKDistinct(int[] nums, int k) {
return atMostK(nums, k) - atMostK(nums, k - 1);
}
private int atMostK(int[] nums, int k) {
int[] map = new int[20001];
int count = 0;
int ret = 0;
int left = 0;
// right从0开始向右移动
for (int right = 0; right < nums.length; ++right) {
if (map[nums[right]] == 0) {
count++;
}
map[nums[right]]++;
// 右移left保证[left, right]只有小于等于k个不同数字
if (count > k) {
while (count > k) {
map[nums[left]]--;
if (map[nums[left]] == 0) {
count--;
}
left++;
}
}
// 计算此时left到right之间以right结尾的subarray个数
ret += right - left + 1;
}
return ret;
}
}