LeetCode: 3. 無重複字元的最長子串
1. 核心思路
1. 滑動視窗
維持一個滑動的視窗,裡面的字元都是唯一的,如果滑動視窗的長度大於以前的最大子串,則更新最大長度
2. 為什麼要用滑動視窗
顯然,易證;由題意可知;略;懂的都懂,不懂的也解釋不了
我們不妨以示例一中的字串 abcabcbb 為例,找出 從每一個字元開始的,不包含重複字元的最長子串,那麼其中最長的那個字串即為答案。對於示例一中的字串,我們列舉出這些結果,其中括號中表示選中的字元以及最長的字串:
以(a)bcabcbb 開始的最長字串為 (abc)abcbb;
以a(b)cabcbb 開始的最長字串為 a(bca)bcbb;
以 ab(c)abcbb 開始的最長字串為 ab(cab)cbb;
以 abc(a)bcbb 開始的最長字串為 abc(abc)bb;
以 abca(b)cbb 開始的最長字串為 abca(bc)bb;
以 abcab(c)bb 開始的最長字串為 abcab(cb)b;
以 abcabc(b)b 開始的最長字串為 abcabc(b)b;
以 abcabcb(b) 開始的最長字串為 abcabcb(b)。
如果我們依次遞增地列舉子串的起始位置,那麼子串的結束位置也是遞增的!這裡的原因在於,假設我們選擇字串中的第 k個字元作為起始位置,並且得到了不包含重複字元的最長子串的結束位置為 r_k。那麼當我們選擇第 k+1個字元作為起始位置時,首先從 k+1到 r_k的字元顯然是不重複的,並且由於少了原本的第 k個字元,我們可以嘗試繼續增大 r_k,直到右側出現了重複字元為止。
(節選自:https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/solution/wu-zhong-fu-zi-fu-de-zui-chang-zi-chuan-by-leetc-2/
2. 具體步驟
- 由上述分析可知,我們要不斷列舉子串的起始位置,然後得出從哪個位置開始的無重複子串的長度最長
- 顯然,並不是開始位置越小,無重複子串越長,只是開始位置越小,可能的 無重複子串越長!
- 結束位置呢?從字串第一個位置開始,不斷向後遍歷,直到有重複的字元為止
- 用一個集合(set)儲存此次遍歷的字元,因為集合特性-無重複元素,集合裡的元素就是當前的無重複子串
- 被結束位置指向的字元放入集合中
- 結束的一次遍歷結束時,判斷一下是否超過以前的最大長度,超過了就更新最大長度
- 如果出現了重複元素呢?怎麼進行下一次遍歷?如何定位到發生重複的元素?
- 答案很簡單,在集合中刪除開始位置指向的字元,然後開始位置向前移動一步,開始下次遍歷。
- 就這?
九折?為什麼刪除開始位置指向的字串就完了啊?重複的元素又不一定是它,刪除後,下次遍歷一樣有重複元素啊 - 確實。但那又有什麼影響呢?下次遍歷有重複,那麼開始位置繼續向前,直到沒有為止
- 這就是一般人的思維和計算機思維的區別
- 在 滑動視窗 的分析中可知結束位置不變的話,最大長度是不會變的,只要有重複元素,結束位置就不會變
- 首先你要知道人是如何解決問題的,然後想到怎麼教計算機用類似的方法解決
- 當然,大部分人想到的都是 暴力解法 ,暴力解法永遠是最後一個選項,如果你把自己當做一個合格的猿類
3. 實現程式碼
public int lengthOfLongestSubstring(String s) {
Set<Character> set = new HashSet<>();
int n = s.length();
int start = 0, end = -1, max =0;
for(;start<n;start++){
// end + 1表示下一個要新增的字元的下標
// 初始值為-1,因為最開始要新增的字元下標為0,即下一個位置為0
while (end+1 < n && !set.contains(s.charAt(end+1))){
set.add(s.charAt(end +1));
end ++;
}
// 遍歷完一個起始位置,多個結束位置後,判斷是否超過以前的最大值
max = Math.max(max, end-start +1);
// 以這個字元開頭的所有子串遍歷完了,刪除後遍歷下一個
set.remove(s.charAt(start));
}
return max;
}
def lengthOfLongestSubstring(self, s: str) -> int:
c_set = set()
start, end, ans, n = 0, -1, 0,len(s)
for start in range(0, n):
while end+1 < n and s[end+1] not in c_set:
c_set.add(s[end+1])
end += 1
ans = max(ans, end-start+1)
c_set.remove(s[start])
return ans
人生苦短,我用Py
4. 最後總結
這是我的第一篇LeetCode題解,為什麼要寫這東西?加深自己印象,幫助他人解題
這題 的突破口是 要想到 從每一個字元開始的,不包含重複字元的最長子串,然後想到滑動視窗
計算機專業的對滑動視窗應該很熟悉了,那麼滑動視窗在計算機相關領域的哪些地方有實際運用呢?
沒經過任何訓練,沒得到任何提示就能想到這解法的確實厲害。