题意:
Given a string, find the length of the longest substring without repeating characters.
Examples:
Given "abcabcbb"
, the answer is "abc"
, which the length is 3.
Given "bbbbb"
, the answer is "b"
, with the length of 1.
Given "pwwkew"
, the answer is "wke"
, with the length of 3. Note that the answer must be a substring, "pwke"
is a subsequence and not a substring. (Medium)
解法1
自己开始AC的想法,用两重循环,搜索以s[i]开头的字串中的最长无重字串,所以一旦有重复元素出现(利用letters判重),内层的循环就可以直接跳出;
复杂度不会超过O(256(n)),因为内层最对走256步必有重复,可以不再走下去。 时间跑出来是60ms
代码1:
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int result = ;
int letters[] = {};
int tempResult;
for (int i = ; i < s.size(); ++i) {
if (i + result >= s.size()) {
break;
}
tempResult = ;
memset(letters, , sizeof(letters));
letters[s[i]] = ;
for (int j = i + ; j < s.size(); ++j) {
if (letters[s[j]] == ) {
tempResult++;
letters[s[j]] = ;
}
else {
break;
}
}
result = max(result, tempResult);
}
return result;
}
};
解法2
滑动窗口方法
用left记录当前考察的可行字串的最左端,i循环遍历整个字串。
如果遇到不重复元素,则添加进hash表内,并更新最大值;
如果遇到重复元素,将left开始递增,直至跳过重复元素,然后同上操作,将该元素添加进hash表,并更新最大值。
代码2利用unodered_set实现(估计find方法效率又低了,只有72ms), 代码3利用单独开的数组实现,效率更高(16ms)。
盗用网上一幅图,出处不详...
复杂度O(2n) = O(n) ; left,i各自遍历一遍O(2n)
代码2:
class Solution {
public:
int lengthOfLongestSubstring(string s) {
unordered_set<char> hash;
int left = ;
int result = ;
for (int i = ; i < s.size(); ++i) {
if (hash.find(s[i]) != hash.end()) {
while (s[left] != s[i]) {
hash.erase(s[left]);
left ++;
}
hash.erase(s[left]);
left ++;
}
hash.insert(s[i]);
result = max(result, i - left + );
}
return result;
}
};
代码3:
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int letters[] = {};
int left = ;
int result = ;
for (int i = ; i < s.size(); ++i) {
if (letters[s[i]] != ) {
while (s[left] != s[i]) {
letters[s[left]] = ;
left ++;
}
letters[s[left]] = ;
left ++;
}
letters[s[i]] = ;
result = max(result, i - left + );
}
return result;
}
};
解法3
解法3是在2的基础上的一个简单优化, letters数组只用来保存存在与否有些浪费,同时用while循环递增left找到重复元素出现位置下一位效率略低;
可以将letters数组改为存储某个字符最近出现的位置lastIndex[256],这样更新left时,直接将left = lestIndex[s[i]] + 1即可, 将 O(2n)变为真正的一次循环 O(n)
代码4:
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int lastIndex[]; // 上次出现该字符的下标
int left = ; //当前维护的字串的最左端
int result = ;
memset(lastIndex, -, sizeof(lastIndex));
for (int i = ; i < s.size(); ++i) {
if (lastIndex[s[i]] >= left) { //在这个字串内有重复 >= left
left = lastIndex[s[i]] + ;
}
lastIndex[s[i]] = i;
result = max(result, i - left + );
}
return result;
}
};