本题的思路是定义两个指针,left和idx,用滑动窗口的方式求解。left是滑动窗口左端,idx是滑动窗口右端。首先保持left不动,将idx向右移动,在向右移动的过程中,每移动一格就考察一下窗口内是否包含了t中所有字符(t中字符可能会有重复),如果包含了t中所有字符,那么就将left向右移动,每次移动一格,考察符合条件的s子串的最小窗口。找到最小窗口后,就将该窗口大小和之前的窗口大小作比较,如果该窗口大小更小,则将minL也就是最小窗口长度更新,同时更新最小窗口的左端和右端。同时将left向右再移动一格,使得当前窗口恰好不是符合条件的子串,再将idx向右移动。重复上述过程,直到idx到达s的最右端,同时已经将left收缩为以idx为右端的最小窗口的左端位置。
注意s中可能不包含符合条件子串,所以要判断一下minL的大小来查看是否查找到符合条件子串,没查找到就输出空串,否则输出找到的子串。
思路讲清楚后就是具体实现。
- 本代码在考察窗口内是否包含了t中所有字符时,用了字典的方法,dicS存储当前窗口所包含的每个t中字符的频数,dicT存储t中每个字符的频数,如果dicS中某个字符的频数等于dicT中某个字符的频数,那么就说明该窗口已经满足了该字符的频数,将count++,其中count表示t中多少字符已经被包含在了窗口中(频数也满足)。如果count==lT,也就是count等于t的所有字符个数的时候,就说明该窗口包含了t中所有字符。
- 在找最小窗口时,本代码使用了循环,将left指针一直右移,同时如果s[left]是t中字符,就将dicS中该字符的频数减1。如果某个字符的频数小于了t中的频数,那么就找到了最小窗口。找到最小窗口后,再根据该窗口大小进行全局最小窗口的更新。
总体代码如下:
class Solution:
def minWindow(self, s: str, t: str) -> str:
if not s or not t:
return ""
dicT = Counter(t)
lT = len(dicT)
minL = len(s)+1 # 最佳窗口长度
resultL = 0
resultR = 0
dicS = {}
left = 0
count = 0 # 已经满足条件的字符数量
for idx, schar in enumerate(s):
if schar in t:
if schar in dicS.keys():
dicS[schar] += 1
else:
dicS[schar] = 1
if dicS[schar] == dicT[schar]:
count += 1
# 判断是否子串[left: idx]包含t中所有字符
# 同时将左指针右移,直到最小窗口
while count == lT:
if s[left] in t:
dicS[s[left]] -= 1
# 满足判断条件则说明是最小窗口
if dicS[s[left]] < dicT[s[left]]:
count -= 1
l = idx-left+1
if l < minL:
minL = l
resultL = left
resultR = idx
left += 1
# 不包含t串中所有字符
if minL > len(s):
return ""
return s[resultL: resultR+1]