LeetCode-3-無重複字元的最長子串(longest-substring-without-repeating-characters)
阿新 • • 發佈:2019-05-11
題目描述
Given a string, find the length of the longest substring without repeating characters.
給定一個字串,請你找出其中不含有重複字元的 最長子串 的長度。
示例 1:
輸入: "abcabcbb"
輸出: 3
解釋: 因為無重複字元的最長子串是 "abc",所以其長度為 3。
示例 2:
輸入: "bbbbb"
輸出: 1
解釋: 因為無重複字元的最長子串是 "b",所以其長度為 1。
示例 3:
輸入: "pwwkew" 輸出: 3 解釋: 因為無重複字元的最長子串是 "wke",所以其長度為 3。 請注意,你的答案必須是 **子串** 的長度,"pwke" 是一個子序列,不是子串。
<!--more-->
我的垃圾思路
One
- 剛開始想耍一些"小聰明",看有沒有巧妙的方法解決,當時用了類似於Node的前後'指標'的方式發現有線用例是過不了的
- 後面用到了類似"滑窗"的方法,碰到重複字元則將滑窗寬度歸零,且位置回到被重複字元的下一位置。
- 但會出現死迴圈,因為沒把之前被重複的字元
remove
掉 -- 後面發現只remove掉那個重複的字元的話,有些沒有重複的字元在回退之後再次計算的時候,會發生混亂<font color=grey size=2>(回退後再次走到之前不重複的字元時候,因為hash
表中已經在第一次put
- 所以上面把滑窗歸零的時候是真正的歸零,包括存資料的hash表
上面方法應該是 $ O(n^2) $ ,因為會發生例如
abcdea
在最後a
發生重複,就會完全回退到b
位置---so low ;提交記錄耗時大概80+ms
Two
- 第二個方法是也兩個指標類似滑窗, k指標一直前進,發生重複時j指標移動到被重複字元的下一位置,但是隻能往右移動,不能回退
- 將
map<Character,Integer>
中存放的之前被重複字元的value(即字元所在的索引)換為當前發生重複的字元位置 -- 不是被重複字元 - 迴圈中一直保持
max
最大 - 當有其中一個指標到達終點時,就可以退出了 ;由於
j,k
max+1
Three
- 第二種方法發現 k一直在++,其實就相當於for迴圈的 i++,所以就換成for迴圈了 -- 複雜度應該是 $ O(n) $
Two和Three 提交的耗時6ms,佔用記憶體35M--佔用記憶體竟然超過了 100%的java使用者ヽ(°◇° )ノ
我的垃圾程式碼
package com.dasnnj.practice.medium;
import java.util.HashMap;
import java.util.Map;
/**
* Description <p> TODO:
* 給定一個字串,請你找出其中不含有重複字元的 最長子串 的長度。
* <p>
* 示例 1:
* <p>
* 輸入: "abcabcbb"
* 輸出: 3
* 解釋: 因為無重複字元的最長子串是 "abc",所以其長度為 3。
* 示例 2:
* <p>
* 輸入: "bbbbb"
* 輸出: 1
* 解釋: 因為無重複字元的最長子串是 "b",所以其長度為 1。
* 示例 3:
* <p>
* 輸入: "pwwkew"
* 輸出: 3
* 解釋: 因為無重複字元的最長子串是 "wke",所以其長度為 3。
* 請注意,你的答案必須是 子串 的長度,"pwke" 是一個子序列,不是子串。</p>
*
* @author DASNNJ <a href="mailto:[email protected]"> [email protected] </a>
* @date 2019-05-08 13:17
*/
public class LongestSubstringWithoutRepeatingCharacters {
public static void main(String[] args) {
LongestSubstringWithoutRepeatingCharacters l = new LongestSubstringWithoutRepeatingCharacters();
System.out.println(l.lengthOfLongestSubstring(""));
}
public int lengthOfLongestSubstring(String s) {
//One
/*char[] chars = s.toCharArray();
Map<Character, Integer> map = new HashMap<>(8);
//計算非重複字元的長度
Integer len = 0;
int max = 0;
//索引
Integer index;
for (int i = 0; i < chars.length; i++) {
//如果是正常進行
//如果重複
if ((index = map.get(chars[i])) != null) {
//回退到重複的位置,從重複位置的下一位重新算,相當於捨棄兩個重複字元中的第一個
i = index;
//長度歸零
len = 0;
//將map清空,從重複位置的下一位置重新計算 -- 清空是因為第一個重複的在上面提到是相當於捨棄,不清空的話會影響下次迴圈的判斷
map.clear();
} else {
//沒重複當然正常put 正常++
map.put(chars[i], i);
len++;
}
//每次迴圈都保持max最大
if (len > max) {
max = len;
}
}
return max;*/
if ("".equals(s)) {
return 0;
}
char[] chars = s.toCharArray();
//j k,用於Two方法的兩個指標---後面發現直接用for迴圈即可
int j = 0, k = 0, max = 0;
Integer ele;
Map<Character, Integer> sets = new HashMap<>(16);
//Three
for (int m = 0; m < chars.length; m++) {
//如果發生重複
if ((ele = sets.get(chars[m])) != null) {
// j指標指向兩個重複字元中的第一個的下一位置,j指標不能後退,只能前進,所以下面有個判斷
if (j < ele + 1) {
j = ele + 1;
}
}
//不重複則是正常put,重複情況1.將j指標指向原字元的下一位2.用新字元替換掉map中原字元(主要是為了替換map中key為此字元 的value值也就是索引)
sets.put(chars[m], m);
//每次迴圈保持max最大
if (max < m - j) {
max = m - j;
}
}
//Two 原理同Three
/*while (j < chars.length && k < chars.length) {
if ((ele = sets.get(chars[k])) != null) {
if (j < ele + 1) {
j = ele + 1;
}
}
sets.put(chars[k], k);
k++;
if (max < k - j) {
max = k - j;
}
}*/
return max + 1