LeetCode:3. 無重複字符的最長子串Java解法

1.題目

給定一個字符串,請你找出其中不含有重複字符的 最長子串 的長度。

示例 1:

輸入: “abcabcbb”
輸出: 3
解釋: 因爲無重複字符的最長子串是 “abc”,所以其長度爲 3。
示例 2:

輸入: “bbbbb”
輸出: 1
解釋: 因爲無重複字符的最長子串是 “b”,所以其長度爲 1。
示例 3:

輸入: “pwwkew”
輸出: 3
解釋: 因爲無重複字符的最長子串是 “wke”,所以其長度爲 3。
請注意,你的答案必須是 子串 的長度,“pwke” 是一個子序列,不是子串。

來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/longest-substring-without-repeating-characters
著作權歸領釦網絡所有。商業轉載請聯繫官方授權,非商業轉載請註明出處。

2.解法概述

該題目有三種解法:
(1)暴力法(大力出奇跡?):先找出所有的子串(時間複雜度爲O(n^2)),再判斷字串是否有重複字符(可以用HashSet,時間複雜度爲O(n))。總的時間複雜度爲O(n^3).
(2)滑動窗口:分爲左指針i和右指針j, 同樣有一個HashSet. j向右移動, 如果set中無重複字符, 則將j所指字符加入set; 如果j所指字符重複, 則將i所指字符從set中去除, 同時i向右移動, 直至j所指字符不重複. 因爲i, j 兩個指針移動, 所以時間複雜度最大爲O(2n).
(3)滑動窗口優化:在(2)的基礎上, 當j所指字符重複時, i直接移動到子串中重複的字符的位置的後一位, 然後j繼續向後, 從而時間複雜度優化爲O(n).

3.暴力法

    //方法一:暴力法
    public int lengthOfLongestSubstring(String s) {
        int n = s.length();
        int ans = 0;
        //找到所有子串
        for (int i = 0; i < n; i++)
            //注意,調用allUnique(s, i, j)時取不到j,這樣j-i纔是字串長,所以j <= n
            for (int j = i + 1; j <= n; j++)
                //計算不重複的最長子串
                if (allUnique(s, i, j)) ans = Math.max(ans, j - i);
        return ans;
    }

    //計算子串是否有重複字符
    public boolean allUnique(String s, int start, int end) {
        Set<Character> set = new HashSet<>();
        //取不到end即j的下標
        for (int i = start; i < end; i++) {
            //String.charAt(int index) 返回指定索引的 char值。
            Character ch = s.charAt(i);
            if (set.contains(ch)) return false;
            set.add(ch);
        }
        return true;
    }

4.滑動窗口

    //方法二:滑動窗口   時間複雜度:O(2n)
    public int SlidingWindow(String s){

        int n = s.length();
        int max = 0,i = 0,j = 0;

        Set<Character> set = new HashSet<>();

        while (i < n && j < n){
            // 如果j下標處不重複,向後擴展滑動窗口
            if ( !set.contains(s.charAt(j)) ){
                //j下標處字符加入HashSet並且向後+1
                set.add(s.charAt(j++));
                //計算長度, j-i 纔是字串長度
                max = Math.max(max, j - i);
            }
            //j下標處重複,滑動窗口i向後+1
            else{
                //將set集合中的i下標字符移除,並且+1,在下一次循環中檢查j下標處字符是否重複
                set.remove(s.charAt(i++));
            }
        }

5.滑動窗口優化

    //方法三 優化的滑動窗口
    public int OptimizedSlidingWindow(String s){

        int n = s.length();
        int max = 0;
        // 字符Character是鍵,下標Integer是值, 記錄的是每一個字符最後出現的位置
        Map<Character, Integer> map = new HashMap<>();

        //嘗試向後滑動窗口,i在左,j在右
        for (int i = 0, j = 0; j < n; j++) {
            //如果j在當前滑動窗口中,直接將i移到重複的字符後一個
            if (map.containsKey(s.charAt(j))){
                //爲何這裏取最大值?
                //因爲在i之前的不重複字符仍在map中,如果這時出現它(如下面的a),i會回溯,
                // 這是爲了防止i回溯
                //比如當s="abba"時,
                i = Math.max(map.get(s.charAt(j))+1, i);
                //i = map.get(s.charAt(j)) + 1;
            }
            //不在,就將j加入map(也可以是更新位置)
            map.put(s.charAt(j), j);

            //計算長度
            max = Math.max(max, j-i+1);
        }

        return max;
    }

6.測試代碼

    @Test
    public void test(){
        int length;
        long startTime1 = System.currentTimeMillis();
        length = lengthOfLongestSubstring("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!\\\"#$%&'()*+,-./:;<=>?@[\\\\]^_`{|}~");
        System.out.println("最長子串:暴力法:長度"+length+"     時間:"+(System.currentTimeMillis() - startTime1)+"ms");
        long startTime2 = System.currentTimeMillis();
        length = SlidingWindow("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!\\\"#$%&'()*+,-./:;<=>?@[\\\\]^_`{|}~");
        System.out.println("最長子串:滑動窗口法:長度"+length+"       時間:"+(System.currentTimeMillis() - startTime2)+"ms");

        long startTime3 = System.currentTimeMillis();
        length = OptimizedSlidingWindow("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!\\\"#$%&'()*+,-./:;<=>?@[\\\\]^_`{|}~");
        System.out.println("最長子串:優化的滑動窗口法:長度"+length+"       時間:"+(System.currentTimeMillis() - startTime3)+"ms");
    }

7.運行結果

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章