先给出labuladong框架
/* 滑动窗口算法框架 */
void slidingWindow(string s, string t) {
unordered_map need, window;
for (char c : t) need[c]++;
int left = 0, right = 0;
int valid = 0;
while (right < s.size()) {
// c 是将移入窗口的字符
char c = s[right];
// 右移窗口
right++;
// 进行窗口内数据的一系列更新
...
// 判断左侧窗口是否要收缩
while (window needs shrink) {
// d 是将移出窗口的字符
char d = s[left];
// 左移窗口
left++;
// 进行窗口内数据的一系列更新
...
}
}
}
下面有几道典型的窗口题,前面几道是很容易套这个模板的。但是后面的两道:求和和求乘积,并不是典型的窗口题,或者说,有一点点小变化。总体遵循的思想应该是:1. 两个while循环(或者第一个循环用for)2. 想清楚什么时候应该收缩左边窗口 3. 想清楚什么时候应该往res返回结果里添加值。
最小覆盖子串
给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 "" 。
注意:如果 s 中存在这样的子串,我们保证它是唯一的答案。
示例 1:
输入:s = "ADOBECODEBANC", t = "ABC"
输出:"BANC"
示例 2:
输入:s = "a", t = "a"
输出:"a"
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 = float('inf')
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 == float('inf'):
return ''
else:
return s[start:start+length]
字符串的排列
给定两个字符串 s1 和 s2,写一个函数来判断 s2 是否包含 s1 的排列。
换句话说,第一个字符串的排列之一是第二个字符串的 子串 。
示例 1:
输入: s1 = "ab" s2 = "eidbaooo"
输出: True
解释: s2 包含 s1 的排列之一 ("ba").
示例 2:
输入: s1= "ab" s2 = "eidboaoo"
输出: False
class Solution:
def checkInclusion(self, s1: str, s2: str) -> bool:
# 问题转化为s2中是否存在一个子串,使得该子串包含s1中所有的元素,而且不包含其他字符。
need = collections.defaultdict(int) # 记录t中字符出现次数
window = collections.defaultdict(int) # 记录窗口中响应的字符出现的次数
for c in s1:
need[c] += 1
left,right = 0,0 # 初始窗口长度为0
valid = 0 # 用于记录window中t中字符是否出现完,比如:t='abc',window='abd',valid就等于2.代表need中应该出现的字符在window中才出现了两个,还没有出现完全
# 记录最小覆盖子串的起始索引及长度
start = 0
length = float('inf')
while right < len(s2):
c = s2[right] # 即将加入window的字符c
right += 1 # 右移窗口
# 窗口内数据的一系列更新
if c in need:
window[c] += 1
if window[c] == need[c]: # window中字符c出现的次数已经达到need所需要的次数时,valid进行更新
valid += 1
# 判断窗口左侧边界是否要收缩(当窗口内的字符数量大于该有的字符数量时,左边需要收缩)
while right-left >= len(s1):
# 在这里判断是否找到了合法子串
if valid == len(need):
return True
# d是将移出窗口的字符
d = s2[left]
# 左移窗口
left += 1
# 进行窗口内数据的一系列更新
if d in need:
if window[d] == need[d]: # 这句话和下面的window[c]-=1不能反,先判断删去的字符c的数量是不是满足need的数量,如果满足,valid将减去1。
valid -= 1
window[d] -= 1
return False
无重复字符的最长子串
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: s = "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:
输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例 3:
输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
示例 4:
输入: s = ""
输出: 0
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
max_length = 0
window = collections.defaultdict(int)
left,right = 0,0
while right < len(s):
c = s[right]
right += 1
window[c] += 1
# 当window中有重复字符的时候开始收缩左边界
while window[c] > 1:
d = s[left]
left += 1
window[d] -= 1
max_length = max(max_length,right-left)
return max_length
要在收缩窗口完成后更新 res
,因为窗口收缩的 while 条件是存在重复元素。
至多包含K个不同字符的最长子串
给定一个字符串 s ,找出 至多 包含 k 个不同字符的最长子串 T。
示例 1:
输入: s = "eceba", k = 2
输出: 3
解释: 则 T 为 "ece",所以长度为 3。
示例 2:
输入: s = "aa", k = 1
输出: 2
解释: 则 T 为 "aa",所以长度为 2。
class Solution:
def lengthOfLongestSubstringKDistinct(self, s: str, k: int) -> int:
max_length = 0
left,right = 0,0
valid = 0
window = collections.defaultdict(int)
while right < len(s):
c = s[right]
right += 1
if window[c] == 0:
valid += 1
window[c] += 1
# 当窗口内字符串包含多于k个不同字符时,收缩窗口左边边界
while valid > k :
d = s[left]
left += 1
if window[d] == 1:
valid -= 1
window[d] -= 1
max_length = max(max_length,right-left)
return max_length
438.找到字符串中的所有字母异位词
给定两个字符串 s 和 p,找到 s 中所有 p 的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。
异位词 指字母相同,但排列不同的字符串。
示例 1:
输入: s = "cbaebabacd", p = "abc"
输出: [0,6]
解释:
起始索引等于 0 的子串是 "cba", 它是 "abc" 的异位词。
起始索引等于 6 的子串是 "bac", 它是 "abc" 的异位词。
示例 2:
输入: s = "abab", p = "ab"
输出: [0,1,2]
解释:
起始索引等于 0 的子串是 "ab", 它是 "ab" 的异位词。
起始索引等于 1 的子串是 "ba", 它是 "ab" 的异位词。
起始索引等于 2 的子串是 "ab", 它是 "ab" 的异位词。
class Solution:
def findAnagrams(self, s: str, p: str) -> List[int]:
window = collections.defaultdict(int)
need = collections.defaultdict(int)
for ch in p:
need[ch] += 1
res = [] # 存储最终返回结果,即各子串的起始index
left,right= 0,0
length = len(p) + 1
valid = 0
while right < len(s):
# 准备移入窗口的字符
c = s[right]
right += 1
if c in need:
window[c] += 1
if window[c] == need[c]:
valid +=1
# 判断何时需要收缩左边窗口边界
while right-left >= len(p):
if valid == len(need):
res.append(left)
# 即将挤出窗口的字符d
d = s[left]
left += 1
if d in need:
if window[d] == need[d]:
valid -=1
window[d] -= 1
return res
209. 长度最小的子数组
给定一个含有 n 个正整数的数组和一个正整数 target 。
找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。
示例 1:
输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。
示例 2:
输入:target = 4, nums = [1,4,4]
输出:1
示例 3:
输入:target = 11, nums = [1,1,1,1,1,1,1,1]
输出:0
class Solution:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
windows = defaultdict(int)
left , right = 0,0
n = len(nums)
res = float('inf')
s = 0
while right < n:
num = nums[right]
s += num
while s >= target:
res = min(res,right-left+1)
num = nums[left]
left += 1
s -= num
right += 1
if res != float('inf') :
return res
else:
return 0
731.乘积小于K的子数组
给定一个正整数数组 nums和整数 k 。
请找出该数组内乘积小于 k 的连续的子数组的个数。
示例 1:
输入: nums = [10,5,2,6], k = 100
输出: 8
解释: 8个乘积小于100的子数组分别为: [10], [5], [2], [6], [10,5], [5,2], [2,6], [5,2,6]。
需要注意的是 [10,5,2] 并不是乘积小于100的子数组。
示例 2:
输入: nums = [1,2,3], k = 0
输出: 0
class Solution:
def numSubarrayProductLessThanK(self, nums: List[int], k: int) -> int:
if k <= 1: return 0
res = 0
left = 0
right = 0
s = 1
while right < len(nums):
num = nums[right]
s = s * num
while s >= k :
num = nums[left]
s = s / num
left += 1
res += (right-left) + 1
right += 1
return res