問題來源
76 . 最小覆蓋子串
給你一個字符串 S、一個字符串 T,請在字符串 S 裏面找出:包含 T 所有字符的最小子串。
示例:
輸入: S = "ADOBECODEBANC" , T = "ABC"
輸出: "BANC"
說明:
如果 S 中不存這樣的子串,則返回空字符串 "" 。
如果 S 中存在這樣的子串,我們保證它是唯一的答案。
大佬解析
代碼
"""
需求:
給你一個字符串 S、一個字符串 T,請在字符串 S 裏面找出:包含 T 所有字符的最小子串。
帶着一張採購清單 去買菜!!!
思想:
1.雙指針+滑動窗口
滑動窗口的思想:
先尋求一個可行的解
在可行解的基礎上,優化這個可行解
優化到不能優化,產生出一個可能的最優解
繼續尋求新的可行解
繼續優化這個可行解
……
在可能的最優解中產生出最優解
窮舉是一根筋,滑動窗口法懂得做選擇
每一個字符最多被遍歷 2 次,即被左、右指針分別走過一次
但窮舉法,有的節點被遍歷次數顯然超過 2
如果說窮舉掃描是一根筋,那滑動窗口法,就懂得做選擇,去掉了重複的工作
"""
class Solution :
def minWindow ( self, s: str , t: str ) - > str :
"""❤組織思路寫代碼
需要一個變量,控制窗口的擴張/收縮
窗口的擴張/收縮 取決於 —— 當前窗口是否找齊所有目標字符
引入 missingType 變量,表示:當前缺失的字符種類數(還要找齊幾種字符)
遍歷前,它的初始值爲 t 字符串的字符種類數
當 missingType == 0 時,代表沒有缺少種類,當前窗口找齊了所有目標字符
是否找齊了字符 取決於 各個字符的缺失情況
用一個 HashMap 存儲各個目標字符,和它對應的缺失個數
t = 'baac' ,則 map 初始值爲 { a: 2, b: 1, c: 1 }
map 中的值是 動態變化 的,比如窗口新納入一個 a ,則 map['a'] 值 -1
map['a'] 爲 0 代表不缺 a 字符了,找齊了
好比 帶着一張採購清單 去買菜
想要買齊 蘿蔔、青菜、豬肉 若干
買了蘿蔔 1 個,蘿蔔的未買數目就減 1 ,減到 0 了就是蘿蔔買齊了
蘿蔔買齊了,未買種類就減 1 ,未買種類減到 0 了就是所有都買齊了
其他幾個變量
left、right 指針,滑動窗口的左右端,初始值都爲 0
minLen :保存最小長度,初始值爲正無窮,爲了讓第一次一定被改寫
resL : 記錄最小子串的起始點
核心思路
主線是 right++ 即擴張窗口,右指針超出 s 串就停止循環
右指針指向的新字符,如果是目標字符,就代表它的缺失個數 - 1
缺失的個數如果因此變 0 ,則缺失的種類就 - 1
擴張窗口,直到當前窗口包含了所有字符,此時 missingType === 0
收縮窗口,並且只要不影響當前窗口包含所有字符,就一直收縮
收縮帶來產生最小串的可能,計算長度,與 minLen 比較
左指針指向的字符,如果是目標字符,它會被捨棄,它的缺失個數 + 1
缺失個數如果因此 > 0 ,缺失的種類數 + 1
右指針步進 1 ,收縮窗口
"""
resL = - 1
min_len = len ( s) + 1
map = { }
missing_type = 0
for i in t:
if i in map :
map [ i] += 1
else :
missing_type += 1
map [ i] = 1
left = 0
for right in range ( len ( s) ) :
right_char = s[ right]
if right_char in map :
map [ right_char] -= 1
if map [ right_char] == 0 :
missing_type -= 1
while missing_type == 0 :
if min_len > right - left + 1 :
min_len = right - left + 1
resL = left
left_char = s[ left]
if left_char in map :
map [ left_char] += 1
if map [ left_char] > 0 :
missing_type += 1
left += 1
return '' if resL == - 1 and min_len == ( len ( s) + 1 ) else s[ resL: resL + min_len]