题目
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();
}