📅  最后修改于: 2023-12-03 15:11:08.895000             🧑  作者: Mango
滑动窗口是一种通过维护窗口内的状态来解决一类字符串/数组问题的算法。其基本原理是维护一个窗口,通过滑动窗口的方式改变窗口的大小,再通过更新窗口内的状态来得到问题的解。
滑动窗口通常用于以下场景:
滑动窗口算法的基本步骤如下:
根据具体问题的不同,滑动窗口算法通常还需要一些额外的技巧,下面我们介绍一些常见的技巧。
最基础的技巧就是判断窗口的状态。对于问题中给出的某种条件,我们需要判断当前窗口是否满足该条件。通常情况下,判断窗口状态的复杂度是$O(1)$的,因为只需要更新窗口左右端点时更新状态即可。
以字符串中最小覆盖子串为例,窗口状态表示为窗口内每个字符出现的次数。对于每个新的右端点,我们需要将对应的字符计数加一,比较当前窗口是否满足条件。对于每个新的左端点,我们需要将对应的字符计数减一,以维护窗口状态。
为了减少不必要的计算,我们可以使用剪枝防止窗口冗余。比如在字符串中最小覆盖子串问题中,如果一个字符在当前窗口内出现的次数小于该字符在目标串中出现的次数,那么该字符在后续的窗口中也不可能成为答案,我们可以直接将窗口右端点移动过去。
在有些问题中,需要计算窗口的大小。通常情况下,窗口大小的计算复杂度是$O(1)$的,因为只需要在左右端点变化时更新即可。
窗口移动即是改变窗口的大小。窗口移动可以是一个单位一个单位地移动,也可以是同时移动多个单位。在字符串中最小覆盖子串问题中,通常会同时移动左右两个端点,而在求数组中最大连续子数组和问题中,通常会一边移动右端点,一边更新答案。
以字符串中最小覆盖子串问题为例,下面给出滑动窗口的实现。
def minCoverSubstring(s, t):
from collections import Counter
window = Counter()
min_len = float('inf')
res = ''
left, right = 0, 0
target = Counter(t)
while right < len(s):
# 更新窗口状态
c = s[right]
window[c] += 1
right += 1
# 若窗口满足条件,则移动左端点
while all(map(lambda x: window[x] >= target[x], target)):
# 更新答案
if right - left < min_len:
min_len = right - left
res = s[left:right]
# 移动左端点
c = s[left]
window[c] -= 1
left += 1
return res