概述
解法
解法一: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
class 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是递增的。
class 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很麻烦
思路见注释:
class 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. Subarrays with K Different Integers解法所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复