LeetCode 76 - Minumum Window Substring
題目
題目連結:https://leetcode.com/problems/minimum-window-substring/
給一字串 S 以及 T,求 S 中最短的子字串包含所有 T 出現過的字元。
範例說明
1 | Example: |
想法
利用 Sliding Window 找出所有包含 T 內所有字元的子字串。
Sliding Window 的詳細介紹可以參考 LeetCode - Substring with Concatenation of All Words
當窗口內沒有包含 T 內的所有字元,可以知道要將窗口變大,也就是將窗口的右側增加。當窗口內包含 T 內的所有字元,則不斷將窗口變小,也就是將窗口的左側增加,直到窗口內不包含 T 內的所有字元。
如此一來,就可以找到所有可能為的子字串,且若能在 O(1) 時間內計算出當前的窗口是否為答案,因為每個字元只會被加入、刪除窗口一次,所以可以知道總時間複雜度為 O(N),N 為字串 S 之長度。
要在 O(1) 的時間內算出窗口內的子字串是否包含 T 內的所有字元,可以利用一個計數器來幫助。
利用 dict[i] 代表字元 i 應該要出現的次數,也就是一開始遍歷 T 內的每一個字元 ch,並將 dict[ch] 加一。接著,開始執行 Sliding Window,當一個字元 S[i] 加入窗口,將 dict[S[i]] 減一;反之,當字元 S[i] 離開窗口,將 dict[S[i]] 加一。
如此一來,若 dict 內的每一個數字皆小於等於 0,代表窗口內已經包含了所有 T 內的字元,所以當前的窗口即可以成為答案。
而要快速的計算 dict 內的每一個數字是否都小於 0,可以利用一變數 total 紀錄當前 dict 內大於 0 的數字數量。當 Sliding Window 執行時,當一字元 S[i] 加入窗口,將 dict[S[i]] 減一,若 dict[S[i]] 變為 0,則 total 減一;反之,當字元 S[i] 離開窗口,將 dict[S[i]] 加一,若 dict[S[i]] 變為 1,則 total 加一。
所以當 total 等於零,可以知道當前的窗口包含所有 T 內的字元,也就是當前的窗口為一組答案。
實作細節
為了讓程式碼看起來更為簡潔,筆者利用了較多的 ++,-- 運算子。
若讀者還不了解 x++、++x 之間的差異:
x++代表將x = x + 1,且回傳還沒加一前的x。++x代表將x = x + 1,且回傳加一過後的x。
所以
1 | int x = 1, y = 1; |
回歸正題,利用變數 l 代表窗口的左邊界,迴圈的變數 i 代表窗口的右邊界。當 s[i] 加入,則應該要執行:
1 | dict[s[i]]--; |
可以簡化為:
1 | if (--dict[s[i]] == 0) total--; |
而當 s[i] 從窗口移出,則應該要執行:
1 | dict[s[l]]++; |
可以簡化為:
1 | if (++dict[s[l++]] == 1) total++; |
而當 total 等於零時,可以知道 l ~ i 為一組合法答案。
程式碼
1 | /** |