解法
解法一:LRU Cache
考虑以A[j]
为结尾的连续数组中有k
个不同的数字有多少种情况,我们把数组A看成请求序列,那么第j个请求时LRU cache的状态可以表示成一个链表,其中第k
个数字的下标
i
k
i_k
ik处就是往回追溯的第k个unique的数字,所以
i
=
i
k
i=i_k
i=ik是使得[i,j]
中有k
个不同数字的最大的那个左边界。
左边界还可以再继续减小,直到遇到第k+1
个不同数字,也就是
i
k
+
1
i_{k+1}
ik+1处的那个数字,这时
[
i
k
+
1
,
j
]
[i_{k+1},j]
[ik+1,j]之间就有k+1
个数字了。
所以以A[j]
为结尾的连续数组中有k
个不同的数字的情况共有:
i
k
−
i
k
+
1
i_k-i_{k+1}
ik−ik+1种。
因此我们可以维护一个大小为K+1
的LRU cache
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47class LinkNode(object): def __init__(self, x,y): self.val = x self.idx = y self.prev = None self.next = None class Solution(object): def subarraysWithKDistinct(self, A, K): """ :type A: List[int] :type K: int :rtype: int """ n = len(A) back,head = LinkNode(0,-1),LinkNode(n+1,n) head.next = back back.prev = head cache = {0:back} ans = 0 K += 1 for i,a in enumerate(A): if a in cache: cache[a].prev.next = cache[a].next if cache[a].next: cache[a].next.prev = cache[a].prev else: back = cache[a].prev cache[a].prev = head cache[a].next = head.next head.next.prev = cache[a] head.next = cache[a] cache[a].idx = i else: cache[a] = LinkNode(a,i) cache[a].prev = head cache[a].next = head.next head.next.prev = cache[a] head.next = cache[a] if len(cache)>K: back.prev.next = None cache.pop(back.val) back = back.prev if len(cache)==K: ans += back.prev.idx-back.idx return ans
解法二:滑动窗口
看了一下网站上给的答案,基本原理跟解法一相似,也需要维护两个值
l
k
l_k
lk和
l
k
−
1
l_{k-1}
lk−1,分别是使得
[
i
,
j
]
[i,j]
[i,j]里有k
个不同的数的最小的左边界。
但是采用的是类似尺取法的思想,那就是随着j增加,
i
k
i_k
ik和
i
k
+
1
i_{k+1}
ik+1是递增的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38class Counter(object): def __init__(self): self.cnt = {} self.num = 0 def add(self,a): self.cnt[a] = self.cnt.get(a,0)+1 if self.cnt[a]==1: self.num += 1 def remove(self,a): self.cnt[a] -= 1 if self.cnt[a] == 0: self.num -= 1 class Solution(object): def subarraysWithKDistinct(self, A, K): """ :type A: List[int] :type K: int :rtype: int """ n = len(A) ans = lk = lk1 = 0 k = Counter() k1 = Counter() for i,a in enumerate(A): k.add(a) k1.add(a) while lk<=i and k.num>K: k.remove(A[lk]) lk += 1 while lk1<=i and k1.num>=K: k1.remove(A[lk1]) lk1 += 1 ans += lk1-lk return ans
效率没有前者高
解法三:优化解法二
参考评论区@lantusky 的答案
解法二的问题是维护两个Counter很麻烦
思路见注释:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33class Solution(object): def subarraysWithKDistinct(self, A, K): """ :type A: List[int] :type K: int :rtype: int """ # former是使得有K个不同数字的最小边界 # latter是最大边界 ans = former = latter = 0 # freq记录了滑动窗口[latter,j]里每个数字的出现频率 freq = {} for a in A: freq[a] = freq.get(a,0) + 1 # 当窗口右边扩大一位之后引入了一个新的数字 if len(freq)==K+1: # 窗口过大,需要增大左边界以减小窗口 # 由于latter是最大的左边界,所以freq[A[latter]]一定为1,否则矛盾 # 所以freq[A[latter]]-1一定为0,那么减小窗口就相当于把它pop掉 freq.pop(A[latter]) latter += 1 # pop完之后窗口[latter+1,j]里一定只有K个不同数字了 # 所以latter+1就是最小的左边界 former = latter # 这里加一句判断是为了把窗口里unique数字小于K的情况去掉 if len(freq)==K: # 求latter的准确值 while freq[A[latter]]>1: freq[A[latter]] -= 1 latter += 1 ans += latter-former +1 return ans
最后
以上就是饱满薯片最近收集整理的关于【Leetcode】992. Subarrays with K Different Integers解法的全部内容,更多相关【Leetcode】992.内容请搜索靠谱客的其他文章。
发表评论 取消回复