这道题目主要难点在于这样一个问题:
a, b, c, d, e, f, c, g, h, ...
我从第一个字符开始检查,已经检查到f了,目前为止还没有出现重复字符:
[a, b, c, d, e, f,] c, g, h, ...
检查到下一个c时,发现它在前面已经出现过了(至于如何判断新字符是否已经出现过,我们在下面讨论):
[a, b,c, d, e, f,c,] g, h, ...
这时应该怎么办?下一次检查哪个子串?
这时将子串的右端点右移是错误的,因为这样子串中依然包括两个c:
[a, b,c, d, e, f,c, g,] h, ...
正确的方式是将子串左端点右移,使它不包含两个成重复的字符:
a, b,c, [d, e, f,c,] g, h, ...
然后再将右端点右移,继续检查:
a, b,c, [d, e, f,c, g,] h, ...
维持三个数:最长子串长度res,每个字符的下标索引m,左边界索引left,形成一个没有重复字符的滑动窗口。
首先初始化所有字符的索引为0,表示没有出现过,然后遍历string,依次给每个字符一个索引m,如果之前没有出现过,就直接计算res长度,如果出现重复字符,就移动左边界到之前这个重复字符位置索引的下一个字符索引,直到循环结束。
if条件语句中为啥要有个m[s[i]] < left,我们用一个例子来说明,当输入字符串为"abbca"的时候,当i=4时,也就是即将要开始遍历最后一个字母a时,此时哈希表中a对应1,b对应3,c对应4,left为2,即当前最长的子字符串的左边界为第二个b的位置,而第一个a已经不在当前最长的字符串的范围内了,那么对于i=4这个新进来的a,应该要加入结果中,而此时未被更新的哈希表中a为1,不是0,如果不判断它和left的关系的话,就无法更新res = i - left + 1这一句代码,那么答案就会少一位,所以需要加m[s[i]] < left。
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int res = 0, m[256] = {0}, left = 0;
for(int i = 0; i < s.size(); i++){
if(m[s[i]] == 0 || m[s[i]] < left){
res = max(res, i - left + 1);
}
else{
left = m[s[i]];
}
m[s[i]] = i + 1;
}
return res;
}
};
vector表示的一种简洁方式
class Solution {
public:
int lengthOfLongestSubstring(string s) {
vector m(256, -1);
int res = 0, left = -1;
for (int i = 0; i < s.size(); ++i) {
left = max(left, m[s[i]]);
m[s[i]] = i;
res = max(res, i - left);
}
return res;
}
};
以及哈希表实现方式
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int res = 0, left = 0, i = 0, n = s.size();
unordered_map m;
for (int i = 0; i < n; ++i) {
left = max(left, m[s[i]]);
m[s[i]] = i + 1;
res = max(res, i - left + 1);
}
return res;
}
};