題目
給你一個字符串 S、一個字符串 T,請在字符串 S 裏面找出:包含 T 所有字符的最小子串。
示例:
輸入: S = "ADOBECODEBANC", T = "ABC"
輸出: "BANC"
說明:
- 如果 S 中不存這樣的子串,則返回空字符串 “”。
- 如果 S 中存在這樣的子串,我們保證它是唯一的答案。
解題思路
定義 left, right 分別指向滑動窗口的左右邊界,子字符串爲 s[left, right) 左閉右開區間。使用 right 指針向右搜索,同時要記錄在 s[left, right) 這個區間覆蓋了多少個 t 中的元素。如果在 s[left,right) 內,覆蓋了所有 t 的元素,說明這個區間是符合要求的一個區間。此時 right 指針再向右移動已經沒有意義。現在要移動 left 指針,直至 s[left,right) 子字符串不能覆蓋 t 。
在 s[left, right) 區間內的元素出現個數應該把 t 中所有的元素都包含,所以我們定義 t_dic 字典變量保存 t 中的元素出現次數,s_dic 字典變量保存 s[left, right) 滑動窗口中的相應字符出現的次數。 count 變量儲存 s[left, right) 閉區間中已經覆蓋了 t 中的多少個元素,如果 cnt == len(t_dic) 說明該子字符串覆蓋了 t 中所有字符,這時已經得到一個可行的覆蓋子串,可以開始收縮窗口了,以便得到最小覆蓋子串。
在移動 left 指針收縮窗口的時候要注意存儲最短的子串,使用的 minLen 變量存儲當前滿足題目要求的最短子串長度。當某個子字符串區間滿足要求時,根據minLen更新最終的最小覆蓋子串。
代碼
class Solution:
def minWindow(self, s: str, t: str) -> str:
need=collections.defaultdict(int)
m = len(s)
n = len(t)
# 滑動窗口中相應出現的字符及其對應的個數
s_dic = {}
# t中出現的字符及其對應的個數
t_dic = {}
# 滑動窗口左右位置,左閉右開區間 [left, right)
left = 0
right = 0
# 滑動窗口中滿足t_dic中條件的字符個數
count = 0
# 記錄最小覆蓋子串的起始索引和長度
start = 0
minlen = float('inf')
# 將t中的字符加入t_dic字典
for i in t:
if i in t_dic:
t_dic[i] += 1
else:
t_dic[i] = 1
while right<m:
# c是將移入窗口的字符
c = s[right]
# 右移窗口
right += 1
# 滑動窗口內數據更新
if c in t_dic:
if c in s_dic:
s_dic[c] += 1
else:
s_dic[c] = 1
if s_dic[c] == t_dic[c]:
count += 1
# 判斷左側窗口是否要收縮
while count==len(t_dic):
# 更新最小覆蓋子串的起始索引和長度
if right-left<minlen:
start = left
minlen = right-left
# d是將移出窗口的字符
d = s[left]
# 左移窗口
left += 1
# 滑動窗口內數據更新
if d in t_dic:
if s_dic[d] == t_dic[d]:
count -= 1
s_dic[d] -= 1
return '' if minlen == float('inf') else s[start:start+minlen]