leetcode 76 最小覆蓋子串

2021-09-29 16:29:37 字數 3335 閱讀 5479

題目描述:

給你乙個字串 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,然後...