1. 程式人生 > 資訊 >微軟 Win10 2004 版將於 12 月停止每月安全或質量更新

微軟 Win10 2004 版將於 12 月停止每月安全或質量更新

無重複字元的最長子串
給定一個字串 s ,請你找出其中不含有重複字元的最長子串的長度。
示例1:
輸入: s = "abcabcbb"
輸出: 3
解釋: 因為無重複字元的最長子串是 "abc",所以其長度為 3。
作者:力扣 (LeetCode)
連結:https://leetcode-cn.com/leetbook/read/top-interview-questions-medium/xv2kgi/
來源:力扣(LeetCode)
著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。

以下是最初比較複雜的想法(是一些廢話)

首先看到這個問題我想到的是通過一個map儲存每個元素最近一次出現的位置。兩個指標解決,一個指標i表示子串的開始,一個指標j表示子串的結尾的下一個元素用以判斷重複元素。
當j指向的字元不是重複元素(即在map中未出現)時j前移將j位置入map。當j指向重複元素時,記錄一下i到j-1的長度。然後i跳至j元素最近一次出現的位置的下一位。
但是這個想法有幾個問題:
1.對於aaa這樣的子串,最長的長度就是1,i,j會指向一個元素,那麼記錄i與j-1的長度(-1)是不合理的
2.對於abcbaef最長子串是cbaef,j遍歷到b時由於重複元素的緣故,i應該跳到b最近一次出現的位置的下一位也就是c,接著j繼續遍歷到a發現a已經出現過,在進行跳轉反而會小於當前位置。這時邏輯就出現了問題。實際上重現遍歷的子串出現的字母應該與之前出現的字母無關。也就是i移動時應該在map中刪除路上出現的字元。
注:上條問題倒是可以用j = Math.max(j, map.get(s.charAt(i)) + 1);解決
其實,這道題的問題還是自己的邏輯沒有縷清,如果j指向的是當前最長子串的下一位,那麼i,j就不應該相遇。但是如果讓j指向i的下一位又需要增串長度的邊界條件。而面對aaa這種串i進行跳轉的時候還會和j相遇,那麼又要對ij相遇的情況做判斷...總之,一開始思路不正確,那麼就會越想越混亂把自己也繞糊塗了。

因此,還是要縷清思路,明確每個變數和邏輯,並保證邏輯不會衝突(其實這個時候我已經繞亂了,直接看的大神題解)

1.依然使用雙指標i,j。j指向當前不重複子串的最後一位。從0開始迴圈到串的最後1位。
2.每次j都會標記當前字元為已訪問,因為j是不重複子串最後一位,所以可以計算i到j的子串長度。
3.重點來了,大神演算法的最精巧地方,若j指向的元素已經訪問過進入迴圈,i向前移動,消去路上所有元素訪問過的記號,也就是說,i會把當前位置到j最近一次出現位置上的元素全部置為未訪問,然後指向下一個位置。j指向的元素也會被置為未訪問,這是j仍然是新串的最後一個元素。重複2操作。
4.做標記的資料結構可以是一個容納全部可能出現的字元的陣列,也可以是一個Map<Character, Boolean>

class Solution {
    public int lengthOfLongestSubstring(String s) {
        int res = 0;
        int i = 0,j = 0;
        boolean[] seen = new boolean[256]; //測試資料字元不會多於擴充套件後的ASCII碼(2^8 = 256)
        while(j < s.length()){
            while(seen[s.charAt(j)]){
                seen[s.charAt(i)] = false;
                i++;
            }
            seen[s.charAt(j)] = true;
            res = Math.max(res, j - i + 1);
            j++;
        }
        return res;

    }
}
接著,可以再換一種思路。

如果發現重複元素,那麼這個元素最後一次出現位置元素與之前的元素對於新子串來說都可以不考慮。既然是之前的元素不考慮,那麼就滿足了佇列的性質。
也就是說佇列提供了暫存子串的功能,只需要每次判斷佇列的長度即可。

public int lengthOfLongestSubstring(String s) {
        Queue<Character> queue = new LinkedList<>();
        char[] array = s.toCharArray();
        int res = 0;
        for(char c : array){
            while(queue.contains(c)){ //只要佇列包括當前字元就進行退隊
                queue.poll();
            }
            queue.offer(c);
            res = Math.max(res, queue.size());
        }
        return res;
    }

所以說做題之前還是要完完整整想清楚邏輯,不然遇到問題會越做越亂。