從零打卡leetcode之day 4--無重複最長字串

帥地發表於2018-08-13

題目描述:

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

示例:

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

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

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


 

 

解題過程:

 

方法一:暴力版本

 

顯然,對於這道題我們可以像以往一樣,遍歷所有子串,對於這個方法,我相信大家都能想到,我就直接貼程式碼了。如下

 

//三個for暴力解決
   public static int lengthOfLongestSubstring(String s) {
       int max = 0;
       int temp = 0;
       char[] arr = new char[s.length()];
       for(int i = 0; i < s.length(); i++){
           arr[i] = s.charAt(i);
           temp = 1;
           for(int j = i + 1; j < s.length(); j++){
               int flag = 0;//用來判斷s[j]是否在arr中
               for(int k = i; k < i + temp; k++){
                   if(arr[k] == s.charAt(j)){
                       flag = 1;
                       break;
                   }
               }
               if(flag == 1){ break;//重複
               } else {
                   arr[i+temp] = s.charAt(j);
                   temp++;
               }
           }
           if(temp > max){
               max = temp;
           }
       }
       return max;
   }

 

前面兩個for迴圈用來遍歷所有子串,第三個for迴圈用來判斷字元s.charAt(j)是否在子串中。

 

 

方法二:各種優化

 

優化策略1:大家想一個問題,對於第三個迴圈,我們在陣列裡查詢該陣列是否擁有某個字元,這個查詢的過程的時間複雜度是O(n),我們是否有其他什麼方法把查詢的過程的複雜度降低到O(1)?

 

前幾次我們都有用過hashMap來進行對映,實際上這裡一樣可以用hashMap來儲存子串,然後再判斷s.charAt(j)是否子串中,這樣我們就可以把查詢過程的複雜度降低到O(1)了。

 

優化策略2:假如給你一個字串:

"abcdca"

 

我們在遍歷子串的過程中,最開始我們從第一個元素'a'開始遍歷,當我們遍歷到'abcd'時,在繼續查詢的時候遇到'c',此時"abcd"裡面已經有'c'了,此時該子串查詢完畢。此時長度為4,繼續下一個子串的查詢。

 

注意:我們繼續下一個子串查詢的時候,是從第二個元素'b'開始的。可是大家想一個問題,真的有必要從第二個元素'b'開始查詢嗎?,假如我們從'b'開始的時候,遍歷到"bcd",繼續遍歷時又會再次遇到'c',此時長度為3。比上一個子串4的長度小。

 

實際上,我們是沒有必要從第二個元素開始查詢的。我們直接從'd'開始查詢就可以了,也就是說,如果 s[j]s[j] 在 [i, j)[i,j) 範圍內有與 j'j 重複的字元。我們再下一次尋找子串時,直接從j'+1的位置開始就行了。如果你不是很理解為啥會這樣的話,可以找一些元素模擬一下勒。

 

優化策略3:我們每次在尋找子串的時候,會把子串放進hashMap裡,假如我們要尋找下一個子串的話,理論上是需要把hashMap裡面的元素給清空,然後再用來放置新的子串的。

 

但實際上,是不需要這樣子的,hashMap裡面的元素是可以重複利用的。先上程式碼吧,然後我在畫圖解釋下優化策略三。如下:

 

 

//用hashMap對映
   public static int lengthOfLongestSubstring2(String s) {
       int max = 0;//儲存最長子串的長度
       //用來記錄子串是從哪個下標開始的
       int i = 0;
       Map<Character, Integer> map = new HashMap<>();

       for(int j = 0; j < s.length(); j++){
           if(map.containsKey(s.charAt(j))){
               //從第一個重複元素的後一個開始
               i = Math.max(map.get(s.charAt(j))+ 1, i);
           }
           //j - i + 1 表示計算此時子串的長度
           max = Math.max(max, j - i + 1);
           map.put(s.charAt(j), j);
       }
       return max;
   }

 

假設字串為"abcba",下面演示hashMap中元素的變化情況。

 

當j = 2。

 

 

當j = 3是,此時出現重複的字元(黃色的表示已經被代替的字元)。

 

當j = 4時。

j = 4時,hashMap有重複的字元a(下標為0的那個),為啥不會把i定位到下標為2的元素上?(因為它都已經遍歷到從下標我為3的那裡了,怎麼可能還會倒回去)

 

這種方法的時間複雜度為O(n)。

 

相關文章