題目描述:
給你乙個字串 s、乙個字串 t,請在字串 s 裡面找出:包含 t 所有字母的最小子串。
示例:
輸入: s =
"adobecodebanc"
, t =
"abc"
輸出:"banc"
說明:解題思路:題目不難理解,就是說要在 s(source) 中找到包含 t(target) 中全部字母的乙個子串,順序無所謂,但這個子串一定是所有可能子串中最短的。
如果我們使用暴力解法,**大概是這樣的:
for
(int i =
0; i < s.
size()
; i++
)for
(int j = i +
1; j < s.
size()
; j++
)if s[i:j] 包含 t 的所有字母:
更新答案
思路很直接吧,但是顯然,這個演算法的複雜度肯定大於 o(n^2) 了,不好。
滑動視窗演算法的思路是這樣:
我們在字串 s 中使用雙指標中的左右指標技巧,初始化 left = right = 0,把索引閉區間 [left, right] 稱為乙個「視窗」。
我們先不斷地增加 right 指標擴大視窗 [left, right],直到視窗中的字串符合要求(包含了 t 中的所有字元)。
此時,我們停止增加 right,轉而不斷增加 left 指標縮小視窗 [left, right],直到視窗中的字串不再符合要求(不包含 t 中的所有字元了)。同時,每次增加 left,我們都要更新一輪結果。
重複第 2 和第 3 步,直到 right 到達字串 s 的盡頭。
這個思路其實也不難,第 2 步相當於在尋找乙個「可行解」,然後第 3 步在優化這個「可行解」,最終找到最優解。左右指標輪流前進,視窗大小增增減減,視窗不斷向右滑動。
下面畫圖理解一下
needs 和 window 相當於計數器,分別記錄 t 中字元出現次數和視窗中的相應字元的出現次數。
初始狀態:
增加 right,直到視窗 [left, right] 包含了 t 中所有字元:
現在開始增加 left,縮小視窗 [left, right]。
直到視窗中的字串不再符合要求,left 不再繼續移動。
之後重複上述過程,先移動 right,再移動 left…… 直到 right 指標到達字串 s 的末端,演算法結束。
如果你能夠理解上述過程,恭喜,你已經完全掌握了滑動視窗演算法思想。至於如何具體到問題,如何得出此題的答案,都是程式設計問題,等會提供一套模板,理解一下就會了。
上述過程可以簡單地寫出如下偽碼框架:
string s, t;
// 在 s 中尋找 t 的「最小覆蓋子串」
int left =
0, right =0;
string res = s;
while
(right < s.
size()
)}return res;
可以用兩個雜湊表當作計數器解決。用乙個雜湊表 needs 記錄字串 t 中包含的字元及出現次數,用另乙個雜湊表 window 記錄當前「視窗」中包含的字元及出現的次數,如果 window 包含所有 needs 中的鍵,且這些鍵對應的值都大於等於 needs 中的值,那麼就可以知道當前「視窗」符合要求了,可以開始移動 left 指標了。
現在將上面的框架繼續細化:
string s, t;
// 在 s 中尋找 t 的「最小覆蓋子串」
int left =
0, right =0;
string res = s;
// 相當於兩個計數器
unordered_map<
char
,int
> window;
unordered_map<
char
,int
> needs;
for(
char c : t) needs[c]++;
// 記錄 window 中已經有多少字元符合要求了
int match =0;
while
(right < s.
size()
) right++
;// window 中的字串已符合 needs 的要求了
while
(match == needs.
size()
) left++;}
}return res;
上述**已經具備完整的邏輯了,只有一處偽碼,即更新 res 的地方,不過這個問題太好解決了,直接看解法吧!
string minwindow
(string s, string t)
right++
;while
(match == needs.
size()
)char c2 = s[left];if
(needs.
count
(c2)
) left++;}
}return minlen == int_max ?
"": s.
substr
(start, minlen)
;}
如果直接甩給你這麼一大段**,我想你的心態是**的,但是通過之前的步步跟進,你是否能夠理解這個演算法的內在邏輯呢?你是否能清晰看出該演算法的結構呢?
這個演算法的時間複雜度是 o(m + n),m 和 n 分別是字串 s 和 t 的長度。因為我們先用 for 迴圈遍歷了字串 t 來初始化 needs,時間 o(n),之後的兩個 while 迴圈最多執行 2m 次,時間 o(m)。
讀者也許認為巢狀的 while 迴圈複雜度應該是平方級,但是你這樣想,while 執行的次數就是雙指標 left 和 right 走的總路程,最多是 2m 嘛。
LeetCode76 最小覆蓋子串
給定乙個字串 s 和乙個字串 t,請在 s 中找出包含 t 所有字母的最小子串。示例 輸入 s adobecodebanc t abc 輸出 banc 說明 字串和雜湊表的問題。hashmap來儲存t字串中個字母元素的出現次數,left right記錄當前子字串的左右下標值,min minleft ...
leetcode 76 最小覆蓋子串
這道題我使用了很笨的方式花了好久解決了,但是時間複雜度太度,只看網上檢視原始碼,不得不說網上的答案基本都是一樣的,但是對於基礎相對薄弱的我來說這些 看起來很是費勁,還用要加強c 基礎的練習才行。思路相對來說不是很難 1 首先構架t字串的hash表,因為字元與ascii碼較好的關係,使用vector陣...
leetcode76 最小覆蓋子串
給定乙個字串 s 和乙個字串 t,請在 s 中找出包含 t 所有字母的最小子串。示例 輸入 s adobecodebanc t abc 輸出 banc 說明 如果 s 中不存這樣的子串,則返回空字串 如果 s 中存在這樣的子串,我們保證它是唯一的答案。典型雙指標滑動串列埠題目,先將t建dict,然後...