给定一个正整数数组 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].
示例 2:
输入:A = [1,2,1,3,4], K = 3
输出:3
解释:恰好由 3 个不同整数组成的子数组:[1,2,1,3], [2,1,3], [1,3,4].
提示:
1 <= A.length <= 20000
1 <= A[i] <= A.length
1 <= K <= A.length
解题思路
首先我们看到数据量是20000
,所以很明显就不能使用暴力破解了。问题一拿到手,不难想到通过滑动窗口处理。
我们定义[i,j)
这个区间内的元素表示最少的构成K
个不同整数的子区间,而[i,k)
表示最少的构成K+1
个不同整数的子区间,那么此时以A[i]
为起始的k
个不同整数组成的子数组个数有k-j+1
个。那么现在我们的问题就变成了,怎么确定我们区间中有K
个不同的整数(最短区间长度情况)呢?
我们可以通过一个dict
存放我们访问到的元素和其个数,如果dict
的数量等于K
的时候,那么此时就是我们的j
位置。下一步,如何确定K+1
不同整数的子区间呢?我们此时只需要判断A[k]
在不在dic
中,如果不在的话,那么此时k
的位置自然就是我们需要的。当我们判断完这样一轮后,我们就可以将A[i]
删除了,然后接着判断下一个A[i]
开头的区间即可。
class Solution:
def subarraysWithKDistinct(self, A: 'List[int]', K: 'int') -> 'int':
n = len(A)
cnt = collections.defaultdict(int)
res, j = 0, 0
for i in range(n):
j = max(j, i)
while j < n and len(cnt) < K:
cnt[A[j]] += 1
j += 1
if len(cnt) < K:
break
k = j
while k < n and A[k] in cnt:
k += 1
res += k - j + 1
cnt[A[i]] -= 1
if cnt[A[i]] == 0:
del cnt[A[i]]
return res
上面代码中有两个细节需要注意,我们的j=max(i,j)
,因为我们的dict
中存储了[i, j)
的数据。我们这里还有一个剪枝操作,就是len(dic) < k
的时候跳出循环。
这个问题还有一个不错的解法。我们可以将这个问题拆解成两个子问题,我们可以先求出最多K
个不同整数的子数组有f_m(K)
个,最多K-1
一个不同整数的子数组有f_m(K-1)
个。那么恰好K
个不同整数的解f_e(K)
就是
为什么要这样做呢?因为最多K
个不同整数的子数组这个问题很容易求。那么这个问题怎么求呢?我们同样定义两个指针i
和j
,表示[i,j]
区间。通过和之前解法相同的思路,我们建立一个dict
去存放访问过的元素,当len(dict)>K
的时候,我们就要从i
开始向j
,删除dict
中对应区间[i,j]
中的元素,直到len(dict)==K
,在这个过程中,我们只需要不断累加区间个数j-i+1
即可(因为[i,j]
可以拆分为j-i+1
个以A[j]
结尾的区间)。
class Solution:
def subarraysWithKDistinct(self, A: 'List[int]', K: 'int') -> 'int':
return self.atMostK(A, K) - self.atMostK(A, K - 1)
def atMostK(self, A, K):
cnt = collections.defaultdict(int)
res = i = 0
for j in range(len(A)):
cnt[A[j]] += 1
while len(cnt) > K:
cnt[A[i]] -= 1
if cnt[A[i]] == 0:
del cnt[A[i]]
i += 1
res += j - i + 1
return res
reference:
https://leetcode.com/problems/subarrays-with-k-different-integers/discuss/234482/JavaC%2B%2BPython-Sliding-Window-with-Video
我将该问题的其他语言版本添加到了我的GitHub Leetcode
如有问题,希望大家指出!!!