題目
https://leetcode.com/problems/longest-substring-without-repeating-characters/
查找一個字符串的子串,子串中沒有重複字符
例:
"abcabcbb" 返回 "abc"
"bbbbb" 返回 "b"
"pwwkew" 返回 "wke"
分析:
看到題目第一眼想到的是KMP算法,同樣是查找子串。
KMP是查詢固定的子串的位置,對子串可以進行預處理。 現在這算法要的是查找的子串長度不固定。所以會有所區別。
不過這算法與查找子符串算法一樣,都想在遍歷一次解決問題,目前想到的有兩種方法。
算法1, 基本算法:
從頭遍歷
對每一個字符往回遍歷查找與當前字符出現的最新一次位置。
計算子串長度
如果比歷史長度大,記錄起來
查找最引 lastIndex變量來存儲匹配的位置,以簡化代碼
int lengthOfLongestSubstring(string s) { int lastMax = 0; int lastIndex = -1; for(int i = 0; i < s.length(); i++){ for(int j = i-1; j > lastIndex; j--){ if(s[i] == s[j]){ lastIndex = j; break; } } if(i- lastIndex> lastMax){ lastMax = i- lastIndex; } } return lastMax > 0 ? lastMax : s.length(); }
與KMP算法的思路一樣,算法1有個明顯的問題就是第二個for循環,很浪費查找進度。如果第二個循環平均計算m次,那整個算法複雜訂爲 o(m*n)
對算法1的改進主要從優化第二個for循環入手,解決思路: 以空間換時間以避免不必要的計算
算法2 空間換時間
加入緩存,存儲所有字符上一次出現的位置。
計算長度時直接索引查找,時間複雜度爲o(1)。
整體時間複雜度就爲o(n)
int lengthOfLongestSubstring(string s) {
int lastMax = 0;
int lastIndex = -1;
//加入緩存, 記錄字符出現的上一個位置
int mapIndex[0x100];
memset(mapIndex, -1, sizeof(mapIndex));
for(int i = 0; i < s.length(); i++){
if(mapIndex[s[i]] >= lastIndex){
lastIndex = mapIndex[s[i]];
}
if(i- lastIndex> lastMax){
lastMax = i- lastIndex;
}
mapIndex[s[i]] = i;
}
return lastMax > 0 ? lastMax : s.length();
}