一、最小覆盖子串 76
https://leetcode-cn.com/problems/minimum-window-substring/submissions/
给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 “” 。
注意:
对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
如果 s 中存在这样的子串,我们保证它是唯一的答案。
1、分析
浅浅分析一下,我们需要找到s字符串中的一个子串,该子串能够包含t中的字符,而且字符的数量要和t中相应字符的数量一样。
我们先用字典统计出t中所有字符以及对应的数量,然后采用滑动窗口去记录访问到的s子串的相应字符数量。用valid标记t中字符出现次数满足条件的情况,若valid与t中字符数相等了,说明t中每个字符都在当前子串中出现了,而且这些字符的数量都是大于等于他们在t中的数量的。
此时就应该进行窗口的缩小了,在窗口缩小的过程中,不断更新子串的起始索引和长度,方便结果的返回。
具体可以看代码和注释。
2、代码
class Solution:
def minWindow(self, s: str, t: str) -> str:
need = Counter(t) #统计t中所有字符的数量
window = collections.defaultdict(int) #初始化滑动窗口
valid = 0 #记录满足条件的字符数
start,min_len = 0,float('inf') #记录最小子串的起始索引和长度
left,right = 0,0 #滑动窗口的边界
while right<len(s): #一直寻找到s的结尾就结束了
c = s[right] #扩大滑动窗口
right += 1
if c in need:
window[c] += 1
if window[c] == need[c]: #发现出现数量达到要求的字符,valid计数加一
valid += 1
while valid == len(need): #开始缩紧滑动窗口的大小
if right-left<min_len: #更新最小子串的起始索引和长度
start = left
min_len = right-left
d = s[left]
left += 1
if d in need:
if window[d]==need[d]:
valid -= 1
window[d] -= 1
return "" if min_len==float('inf') else s[start:start+min_len] #看是否找到了最小子串
二、字符串的排列 567
https://leetcode-cn.com/problems/permutation-in-string/
给你两个字符串 s1 和 s2 ,写一个函数来判断 s2 是否包含 s1 的排列。如果是,返回 true ;否则,返回 false 。
换句话说,s1 的排列之一是 s2 的 子串 。
1、分析
与第一题的模板一样,只是在缩小窗口的循环控制条件处,以及valid判断处有不同。具体可以看代码及注释。
2、代码
class Solution:
def checkInclusion(self, s1: str, s2: str) -> bool:
need = Counter(s1)
window = collections.defaultdict(int)
left,right = 0,0
valid = 0
while right<len(s2):
c = s2[right]
right += 1
if c in need:
window[c] += 1
if window[c] == need[c]:
valid += 1
while right-left==len(s1): #因为要找的是与s1排列相同的子串,所以该子串的长度应该和s1一样
if valid == len(need): #若此时长度一样,且各个字符的数量都满足条件了,可以直接返回true,否则继续滑动窗口去寻找
return True
d = s2[left]
left += 1
if d in need:
if window[d] == need[d]:
valid -= 1
window[d] -= 1
return False
三、找到字符串中所有字母异位词 438
https://leetcode-cn.com/problems/find-all-anagrams-in-a-string/
给定两个字符串 s 和 p,找到 s 中所有 p 的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。
异位词 指由相同字母重排列形成的字符串(包括相同的字符串)。
1、分析
这道题与第二道题非常相似,只是第二题找到一个就行,这道题需要找到所有满足条件的子串。具体见代码。
2、代码
class Solution:
def findAnagrams(self, s: str, p: str) -> List[int]:
res = []
need = Counter(p)
window = collections.defaultdict(int)
left,right = 0,0
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
if right-left == len(p): #一旦滑动窗口的长度和目标字符串相同了,就触发滑动窗口的收缩
if valid == len(need):
res.append(left) #与第二题相比,只是这里不同,记录每一个满足条件子串的起点
d = s[left]
left += 1
if d in need:
if window[d] == need[d]:
valid -= 1
window[d] -= 1
return res
四、无重复字符的最长子串
https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/submissions/
给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。
1、分析
抓住两个要点,不含重复字符,以及要找到最长的子串。具体见代码。
只需要用滑动窗口去记录遇到的字符的出现次数,一旦出现次数超过1的就立刻缩小窗口,所以每次循环时,窗口中的子串都不含有重复字符。
2、代码
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
window = collections.defaultdict(int)
left,right = 0,0
res = 0
while right<len(s):
c = s[right]
right += 1
window[c] += 1
while window[c]>1:
# 只要有重复元素,就缩小窗口大小
d = s[left]
left += 1
window[d] -= 1
res = max(res,right-left)
return res