1. 程式人生 > >LeetCode刷題筆記(三)無重複字元的最長子串

LeetCode刷題筆記(三)無重複字元的最長子串

題目:

給定一個字串,找出不含有重複字元的最長子串的長度。

示例:

    給定 "abcabcbb" ,沒有重複字元的最長子串是 "abc" ,那麼長度就是3。

    給定 "bbbbb" ,最長的子串就是 "b" ,長度是1。

    給定 "pwwkew" ,最長子串是 "wke" ,長度是3。請注意答案必須是一個子串"pwke" 是 子序列  而不是子串。

解題1:

class Solution {
    public int lengthOfLongestSubstring(String s) {
        int len = s.length();
        String result = "";   //最長的不重複子串
        String str = "";      //子串
        for(int i=0;i<len;i++){
            for(int j=i+1;j<=len;j++){
                str = s.substring(i,j);    //巢狀迴圈擷取子串
                if(j==len){                //擷取帶最後進行對比
                    result = result.length()<str.length()?str:result;
                    break;
                }else{                     //如果沒有到最後就判斷下一位是否在子串中
                    if(str.indexOf(s.substring(j,j+1))>-1){
                       result = result.length()<str.length()?str:result;
                       break;
                    }
                }
            }
        }
        return result.length();
    }
}

這是我自己的解法:首先要理解一個思路,如果[i,j]是不重複的,那麼要判斷[i,j+1]是否重複,就只要判斷[j+1]在[i,j]是否存在。個越界檢查。

這裡的計算就是:設'abcabcbb',len=8,

從(0,1)(含左不含右)開始,不等於len,進入else條件,'a'不包括下一個字元'b',依次迴圈,

到了(0,3)的時候,str="abc",包含下一個字元'c',result="abc",跳出迴圈,

進入(1,2),str="b",一直到(1,4),包含下一個字元'b',result長度等於str長度,所以result不變。跳出迴圈,

進入(2,3),str="c",依次迴圈,最後一直到最後str的長度也沒有超過result。最終得出result為abc,所以長度為3

時間複雜度:O(n^2)

空間複雜度:O(n^2)

解法2:

public class Solution {
    public int lengthOfLongestSubstring(String s) {
        int n = s.length();
        Set<Character> set = new HashSet<>();    //存放視窗元素
        int ans = 0, i = 0, j = 0;              //ans不重複子串長度,i是頭節點,j是尾節點
        while (i < n && j < n) {
            // try to extend the range [i, j]
            if (!set.contains(s.charAt(j))){      //不包含下一位字元就把尾節點後移一位,視窗+1,並且判斷最長子串的長度
                set.add(s.charAt(j++));
                ans = Math.max(ans, j - i);
            }
            else {
                set.remove(s.charAt(i++));        //包含下一位就把頭節點後移到尾節點的前一位,視窗重新置為1
            }
        }
        return ans;
    }
}

滑動視窗思想:其實這個和上面的思路是一樣的,就是判斷[i,j]和[j+1]是否重複的問題

若重複:頭節點後移到尾節點前一位,滑動視窗大小重定義為1

若不重複:滑動視窗頭不變,結尾+1,整個視窗加大1個單位。繼續比較下一個。

時間複雜度:O(n^2);

空間複雜度:O(n);

解法3:

public class Solution {
    public int lengthOfLongestSubstring(String s) {
        int n = s.length(), ans = 0;
        Map<Character, Integer> map = new HashMap<>();    //存放字元陣列和索引,key:char,value:index
        for (int j = 0, i = 0; j < n; j++) {
            if (map.containsKey(s.charAt(j))) {
                i = Math.max(map.get(s.charAt(j)), i);   //包含下一個字元就把i移到相同字元的後一個位置
            }
            ans = Math.max(ans, j - i + 1);             //判斷長度是否要改變
            map.put(s.charAt(j), j + 1);                //把字元和索引放進map,重複的會進行覆蓋
        }
        return ans;
    }
}

這是一直利用HashMap解題思路:其實也是判斷[i,j]和[j+1]的問題。

舉個例子:s="abcbadca"; i= 0;j=0

依次把{a:1,b:2,c:3}存入map,此時ans = 3,i=0,j=3;

下一次進入的時候,因為‘b'已存在map中,所以i會被設為2,即字元陣列中第一個b的後一個位置。同時map裡面的b對應的值會被更新為4,然後判斷ans的值,同時map裡面的b對應的值會被更新為4。

依次計算,就可以把最長不重複子串的長度得出。這裡只有一個迴圈,map的查詢時間複雜度為O(1),比之前的兩種方法都快。

時間複雜度:O(n);

空間複雜度:O(1)       //因為字元的總個數是固定的,所以map的長度也是固定的常數;

解法4:

public class Solution {
    public int lengthOfLongestSubstring(String s) {
        int n = s.length(), ans = 0;
        int[] index = new int[128]; // current index of character
        for (int j = 0, i = 0; j < n; j++) {
            i = Math.max(index[s.charAt(j)], i);
            ans = Math.max(ans, j - i + 1);
            index[s.charAt(j)] = j + 1;
        }
        return ans;
    }
}

這個解法和上面的HashMap的解法其實是一樣的,只不過這種解法是把之前map的key作為陣列的索引,value作為陣列的值。因為字元char會被轉為int值(根據ASCII碼錶對應)。不過這種的優勢在於只要一個128的陣列,而不是map的結構。

時間複雜度:O(n);

空間複雜度:O(1);