滑动窗口的动态演示:
【力扣209题】
给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的连续子数组。如果不存在符合条件的连续子数组,返回 0。
示例:
输入: s = 7, nums = [2,3,1,2,4,3]
输出: 2
解释: 子数组 [4,3] 是该条件下的长度最小的连续子数组。
滑动窗口演示图
演示题解代码:
def minSubArrayLen(s: int, nums: List[int]) -> int:
summation = 0
L, R = 0, -1
optim = len(nums) + 1
while R < len(nums):
while R < len(nums):
R += 1
if R < len(nums):
summation += nums[R]
if summation >= s:
optim = min(optim, R - L + 1)
break
if R == len(nums):
break
while L < R:
summation -= nums[L]
L += 1
if summation >= s:
optim = min(optim, R - L + 1)
else:
break
return optim if optim != len(nums) + 1 else 0
力扣第3题:
无重复字符的最长子串
上题的模板可以总结为:
初始化窗口端点L,R,一般L为0,R为1
初始化最优值
while R < len(Array):
while R < len(Array):
R += 1 #移动右端点
if R < len(Array):
更新状态
if 状态满足条件:
可选的更新最优值的位置
break #一旦满足条件即跳出
if R == len(Array): # 若循环是由于移动到数组末尾结束,则停止整个程序。因为之后已经不再有可能的解
break
while L < R:
更新状态 # 移动左端点,需要更新状态
L += 1
if 状态满足条件:
可选的更新最优值的位置
else: # 一旦窗口所在区间不再满足条件即跳出,去移动右端点
break
可选的对于L,R端点的后续处理
return 最优值
用以上模板可以写出相应的题解(无重复元素的最长子串):
def lengthOfLongestSubstring(s: str) -> int:
L, R = 0, -1
optim = 0
status = set()
while R < len(s):
while R < len(s):
R += 1
if R == len(s):
break
if s[R] not in status:
status.add(s[R])
optim = max(optim, R - L + 1)
else:
break
if R == len(s):
break
while L < R:
if s[L] != s[R]:
status.remove(s[L])
L += 1
else:
L += 1
break
return optim
茴香豆的四种写法:
力扣3题的另外相关模板集合:
模板1:使用defaultdict
class Solution:
def lengthOfLongestSubstring(self, s):
"""
:type s: str
:rtype: int
"""
from collections import defaultdict
lookup = defaultdict(int)
start = 0
end = 0
max_len = 0
counter = 0
while end < len(s):
if lookup[s[end]] > 0:
counter += 1
lookup[s[end]] += 1
end += 1
while counter > 0:
if lookup[s[start]] > 1:
counter -= 1
lookup[s[start]] -= 1
start += 1
max_len = max(max_len, end - start)
return max_len
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
# Step 1: 定义需要维护的变量, 本题求最大长度,所以需要定义max_len, 该题又涉及去重,因此还需要一个哈希表
max_len, hashmap = 0, {}
# Step 2: 定义窗口的首尾端 (start, end), 然后滑动窗口
start = 0
for end in range(len(s)):
# Step 3
# 更新需要维护的变量 (max_len, hashmap)
# i.e. 把窗口末端元素加入哈希表,使其频率加1,并且更新最大长度
hashmap[s[end]] = hashmap.get(s[end], 0) + 1
if len(hashmap) == end - start + 1:
max_len = max(max_len, end - start + 1)
# Step 4:
# 根据题意, 题目的窗口长度可变: 这个时候一般涉及到窗口是否合法的问题
# 这时要用一个while去不断移动窗口左指针, 从而剔除非法元素直到窗口再次合法
# 当窗口长度大于哈希表长度时候 (说明存在重复元素),窗口不合法
# 所以需要不断移动窗口左指针直到窗口再次合法, 同时提前更新需要维护的变量 (hashmap)
while end - start + 1 > len(hashmap):
head = s[start]
hashmap[head] -= 1
if hashmap[head] == 0:
del hashmap[head]
start += 1
# Step 5: 返回答案 (最大长度)
return max_len
力扣1004. 最大连续1的个数 III最大连续1的个数
用上述第一个模板,三个while循环,只需要找到关键的条件即可(即>=0),不满足则break
def longestOnes(A: List[int], K: int) -> int:
L, R = 0, -1
optim = 0
while R < len(A):
while R < len(A):
R += 1
if R == len(A):
break
if A[R] == 0:
K -= 1
if K < 0: #第一次不满足条件
break
else: #满足条件时更新最优值
optim = max(optim, R - L + 1)
if R == len(A):
break
while L < R:
if A[L] == 0:
K += 1
L += 1
if K >= 0:
break
return optim
class Solution:
def minWindow(self, s: str, t: str) -> str:
need = collections.defaultdict(int) # 记录t中字符出现次数
window = collections.defaultdict(int) # 记录窗口中响应的字符出现的次数
for c in t:
need[c] += 1
left,right = 0,0 # 初始窗口长度为0
valid = 0 # 用于记录window中t中字符是否出现完,比如:t='abc',window='abd',valid就等于2.代表need中应该出现的字符在window中才出现了两个,还没有出现完全
# 记录最小覆盖子串的起始索引及长度
start = 0
length = len(s) + 1
while right < len(s):
c = s[right] # 即将加入window的字符c
right += 1 # 右移窗口
# 窗口内数据的一系列更新
if c in need:
window[c] += 1
if window[c] == need[c]: # window中字符c出现的次数已经达到need所需要的次数时,valid进行更新
valid += 1
# 判断窗口左侧边界是否要收缩
while valid == len(need):
# 在这里更新最小覆盖子串
if right-left < length:
start = left
length = right-left
# d是将移出窗口的字符
d = s[left]
# 左移窗口
left += 1
# 进行窗口内数据的一系列更新
if d in need:
if window[d] == need[d]: # 这句话和下面的window[c]-=1不能反,先判断删去的字符c的数量是不是满足need的数量,如果满足,valid将减去1。
valid -= 1
window[d] -= 1
# 返回最小覆盖子串
if length == len(s) + 1:
return ''
else:
return s[start:start+length]