https://labuladong.online/algo/essential-technique/sliding-window-framework/
1. 框架
# 滑动窗口算法框架
def slidingWindow(s: str):
# 用合适的数据结构记录窗口中的数据,根据具体场景变通
# 比如说,我想记录窗口中元素出现的次数,就用 map
# 我想记录窗口中的元素和,就用 int
window = dict()
left = 0
right = 0
while right < len(s):
# c 是将移入窗口的字符
c = s[right]
window[c] = window.get(c, 0) + 1
# 增大窗口
right += 1
# 进行窗口内数据的一系列更新
#...
#/*** debug 输出的位置 ***/
# 注意在最终的解法代码中不要 print
# 因为 IO 操作很耗时,可能导致超时
# print(f"window: [{left}, {right})")
#/********************/
# 判断左侧窗口是否要收缩
while left < right and "window needs shrink":
# d 是将移出窗口的字符
d = s[left]
window[d] -= 1
if window[d] == 0:
del window[d]
# 缩小窗口
left += 1
# 进行窗口内数据的一系列更新
#...
2. 最小覆盖子串
class Solution(object):
def minWindow(self, s, t):
"""
:type s: str
:type t: str
:rtype: str
"""
n = len(s)
if n == 0:
return ""
# 用来判断当前窗口是否满足条件
valid_bit = 0
window_dict = {}
target_dict = {}
start, final_len = 0, n+1
for c in t:
if c in target_dict.keys():
target_dict[c] += 1
else:
target_dict[c] = 1
lo, hi = 0, 0
while hi < n:
ch = s[hi]
hi += 1
if ch in target_dict.keys():
if ch not in window_dict.keys():
window_dict[ch] = 1
else:
window_dict[ch] += 1
if window_dict[ch] == target_dict[ch]:
valid_bit += 1
while valid_bit == len(target_dict):
if hi -lo < final_len:
final_len = hi - lo
start = lo
ch = s[lo]
lo += 1
if ch in target_dict.keys():
window_dict[ch] -= 1
if window_dict[ch] < target_dict[ch]:
valid_bit -= 1
if final_len == n+1:
return ""
else:
return s[start:start+final_len]
3. 找到字符串中的所有字母异位词
https://leetcode.cn/problems/find-all-anagrams-in-a-string/description/
这道题可以按照上面,先扩右边再收缩左边;但第一反应想到的是固定长度的滑动窗口,每次直接lo++ hi++。
但是这里两个地方容易出错:
- len(s) < len§的时候会报错
- 要先在while循环外判断一次valid,然后在每次while结束的时候判断valid,有点繁琐,容易写错
class Solution(object):
def findAnagrams(self, s, p):
"""
:type s: str
:type p: str
:rtype: List[int]
"""
def putChar(d, c):
if c in d:
d[c] += 1
else:
d[c] = 1
res = []
if not s or len(s) < len(p):
return res
valid = 0
lo, hi = 0, len(p)
target_dict = {}
window_dict = {}
for c in p:
putChar(target_dict, c)
for i in range(lo, hi):
if s[i] in p:
putChar(window_dict, s[i])
if window_dict[s[i]] == target_dict[s[i]]:
valid += 1
if valid == len(target_dict):
res.append(lo)
while hi < len(s):
if s[lo] in window_dict:
if window_dict[s[lo]] == target_dict[s[lo]]:
valid -= 1
window_dict[s[lo]] -= 1
if s[hi] in target_dict:
putChar(window_dict, s[hi])
if window_dict[s[hi]] == target_dict[s[hi]]:
valid += 1
lo += 1
hi += 1
if valid == len(target_dict):
res.append(lo)
return res
再用先扩后缩的框架写一遍,这种方式更不容易出错
class Solution(object):
def findAnagrams(self, s, p):
"""
:type s: str
:type p: str
:rtype: List[int]
"""
def putChar(d, c):
if c in d:
d[c] += 1
else:
d[c] = 1
res = []
if not s:
return res
valid = 0
lo, hi = 0, 0
target_dict = {}
window_dict = {}
for c in p:
putChar(target_dict, c)
while hi < len(s):
ch = s[hi]
hi += 1
if ch in target_dict:
putChar(window_dict, ch)
if window_dict[ch] == target_dict[ch]:
valid += 1
while valid == len(target_dict):
if hi - lo == len(p):
res.append(lo)
start = lo
ch = s[lo]
if ch in target_dict:
window_dict[ch] -= 1
if window_dict[ch] < target_dict[ch]:
valid -= 1
lo += 1
return res
4 无重复字符的最长子串
这个直接套框架
https://leetcode.cn/problems/longest-substring-without-repeating-characters/description/
class Solution(object):
def lengthOfLongestSubstring(self, s):
"""
:type s: str
:rtype: int
"""
if not s:
return 0
window_dict = {}
lo, hi = 0, 0
res = 0
while hi < len(s):
c = s[hi]
hi += 1
if c not in window_dict or window_dict[c] == 0:
window_dict[c] = 1
res = max(res, hi - lo)
else:
window_dict[c] += 1
while window_dict[c] > 1:
c2 = s[lo]
window_dict[c2] -= 1
lo += 1
return res