1. 程式人生 > 其它 >LeetCode 0003 Longest Substring Without Repeating Characters

LeetCode 0003 Longest Substring Without Repeating Characters

原題傳送門

1. 題目描述

2. Solution 1: Brute force

1、思路
如Q001 中提到的,最樸素的想法自然就是窮舉了。兩層迴圈,外層迴圈設定不重複字串起點start,內層迴圈設定終點end,提供輔助函式判斷s[start, end]是否重複。
2、程式碼實現

public class Solution1 {
    public int lengthOfLongestSubstring(String s) {
        int n = s.length();
        int result = 0;
        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j <= n; j++) {
                if (check(s, i, j)) result = Math.max(result, j - i);
            }
        }
        return result;
    }

    private boolean check(String s, int start, int end) {
        Set<Character> set = new HashSet<>();
        for (int i = start; i < end; i++) {
            char c = s.charAt(i);
            if (set.contains(c)) return false;
            set.add(c);
        }
        return true;
    }
}

time complexity: 遍歷字元兩層迴圈 O(n^2), check函式O(n), 綜合O(n^3)
space complexity: O(n)

3. Solution 2

1、思路: 雙指標 + Set做滑動視窗
用兩個變數start、end分別表示不重複子串的起點和終點,使用Set做滑動視窗暫存不重複的子串字元,res儲存全域性結果。
a) 初始態

int start = 0, end = 0, res = 0;
Set<Character> window = new HashSet<>();

b) 各變數更新
end逐個遍歷s中的字元,若s[end] not in window,window add s[end],end指向下一個字元,更新res取max{res, window.size}。若 s[end] in window,window中移除s[start],start指向下一個字元。
2、程式碼實現

public class Solution2 {

    // 使用Set做視窗
    public static int lengthOfLongestSubstring(String s) {
        int n = s.length();
        Set<Character> window = new HashSet<>();
        int res = 0, start = 0, end = 0;
        while (start < n && end < n) {
            // try to extend the range [i, j]
            if (!window.contains(s.charAt(end))) {
                window.add(s.charAt(end++));
                res = Math.max(res, window.size());
            } else {
                window.remove(s.charAt(start++));
            }
        }
        return res;
    }
}

3. Solution 3

1、思路: 雙指標 + HashMap
用兩個變數start、end分別表示不重複子串的起點和終點,使用HashMap儲存遍歷過的字元,最近出現在s中的下標,res儲存全域性結果。
a) 初始態

int start = 0, end = 0, res = 0;
HashMap<Character, Integer> lastOccurred = new HashMap<>();

b) 各變數更新
end逐個遍歷s中的字元,若s[end] in HashMap, 更新start,start = max{start, lastOccurred.get(s.charAt(end) + 1},這裡+1是因為lastOccurred儲存的是字元的最後一次出現的下標,而start的定義是不重複子串的起點,應該是上一個終點下一個字元。若s[end] not in HashMap,把s[end] 錄入HashMap,lastOccurred[s[end]] = end,更新res,res = max{res, end - start + 1},這裡+1是因為我們需要統計點的個數,end - start只計算了間隔數,可以在數軸上畫畫看。

如圖,取start = 1, end = 4, end - start = 4 - 1 = 3,表示上面的藍色格子有3個,同時,若取點的個數有 {1, 2, 3, 4},點的個數為 end - start + 1 = 4 - 1 + 1 = 4。
2、程式碼實現

public class Solution3 {

    /*
      滑動視窗:在視窗中始終儲存不重複的字母,求視窗的長度並更新到結果中
      start: 不重複子串中第1個字元在s中的下標
      end:   不重複子串中最後一個字元在s中的下標
      HashMap<Character, Integer> lastOccurred: 儲存字元 c 在s中最後一次出現的下標
      難點,兩處`+1`:
      - 第30行: lastOccurred中儲存的是字元c在s中最後一次出現的下標,所以start應該是c後面一個字元,該+1。
      - 第33行: 手繪一個數軸,數軸上的兩個不同點a, b之間的間隔數為(b - a),可是,a,b間點的個數為(b - a + 1)。
      以`pwwkew`為例
      index: 0 1 2 3 4 5
      char:  p w w k e w
      滿足要求的子串為`wke`,而程式碼計算出結果選取的是`kew`,此時start = index(k) = 3, end = 5, res = end - start + 1 = 3
     */
    public int lengthOfLongestSubstring(String s) {
        if (s == null || s.length() == 0) return 0;
        HashMap<Character, Integer> lastOccurred = new HashMap<>();
        int res = 0;
        for (int start = 0, end = 0; end < s.length(); end++) {
            if (lastOccurred.containsKey(s.charAt(end))) {
                start = Math.max(start, lastOccurred.get(s.charAt(end)) + 1);
            }
            lastOccurred.put(s.charAt(end), end);
            res = Math.max(res, end - start + 1);
        }
        return res;
    }
}

time complexity: O(n)
space complexity: O(n)

4. Solution 4

1、思路: 雙指標 + Array
繼續優化上一個解答,使用陣列做自定義雜湊表,字元c 轉換成 ASCII表中對應整數(int) c,並以此作為陣列下標,陣列中的值儲存字元最後出現的下標 。
2、程式碼實現

public class Solution4 {
    /*
      使用自定義雜湊表
     */
    public static int lengthOfLongestSubstring(String s) {
        int[] lastOccurred = new int[128];
        int res = 0;

        for (int start = 0, end = 0; end < s.length(); end++) {
            if (lastOccurred[s.charAt(end)] >= start) {
                start = lastOccurred[s.charAt(end)];
            }
            res = Math.max(res, end - start + 1);
            lastOccurred[s.charAt(end)] = end + 1;
        }
        return res;
    }
}

time complexity: O(n)
space complexity: O(n)