问题链接
题目解析
求字符串的最长无重复子串。
解题思路
第一个问题是子串,注意是连续的。
建立一个符号哈希数组 \(in[256]\),代表该符号时候出现过,256大小是因为ASCII表共能表示256个字符。初始化为0,代表未出现,当 \(in[i] > 0\) 时,表示该字符最近出现的索引位置+1。
定义变量\(res\):记录最长无重复子串长度;\(left\):包含当前字符的最长无重复子串的起始位置。遍历字符串,遇到未出现的字符(\(in[s[i]] == 0\))时,更新 \(res\);注意另一种情况,当哈希表中的值小于 \(left\),原因是出现过重复的字符,导致 \(left\) 的位置更新了(变大),如果又遇到了新的字符或者虽然重复,但重复位置在 \(left\) 之前,也需要更新 \(res\)。否则的话,就要更新 \(left\) 的位置为 \(in[s[i]]\)。
\(left\) 更新问题:注意理解更新 \(left\) 的条件,一方面理解:无法更新 \(res\),就要更新 \(left\),二者是对立的;另一方面,直接理解更新条件:\(in[s[i]] > 0 && in[s[i]] >= left\)。在当前字符重复的情况下,且重复位置在 \(left\) 的右边时,更新 \(left\)。
注意每次都需要更新 \(in[s[i]] = i+1\),目的是下次再遇到此字符时,直接将 \(left\) 赋值为 \(in[s[i]]\),保证无重复。
参考代码
class Solution {
public:
int lengthOfLongestSubstring(string s) {
vector<int> in(256, 0);
int res = 0, left = 0;
for (int i = 0; i < s.size(); i++) {
if (in[s[i]] == 0 || in[s[i]] < left) {
res = max(res, i-left+1);
}
else
left = in[s[i]];
in[s[i]] = i+1;
}
return res;
}
};
思路一样,有一种更精简的写法。\(res\) 和 \(left\) 的定义不变,\(in[s[i]]\) 的值稍微变化,变为字符最近出现的索引位置,没有+1。
先更新 \(left\),再更新 \(in[s[i]]\),最后更新 \(res\),妙不可言~
class Solution {
public:
int lengthOfLongestSubstring(string s) {
vector<int> in(256, -1);
int res = 0, left = -1;
for (int i = 0; i < s.size(); ++i) {
left = max(left, in[s[i]]);
in[s[i]] = i;
res = max(res, i - left);
}
return res;
}
};
解法二:set
运用集合(set),其特点是不包含重复元素。思路其实还是没有变,遍历字符串,把未出现过的字符都放入set中,更新结果res;如果遇到重复字符,则从左边开始删字符,直到删到集合中没有重复的字符停止。
class Solution {
public:
int lengthOfLongestSubstring(string s) {
set<char> t;
int res = 0, left = 0, right = 0;
while (right < s.size()) {
if (t.find(s[right]) == t.end()) {
t.insert(s[right++]);
res = max(res, (int)t.size());
} else {
t.erase(s[left++]);
}
}
return res;
}
};
本文版权归作者AlvinZH和博客园所有,欢迎转载和商用,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.