3. 无重复字符的最长子串 滑动窗口+哈希表 共三种解法
https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/
解法一 基于红黑树(set)的遍历法(自己瞎编的
维护一个set,set中存放着到s[i]为止的最长无重复字符子串。
同时维护stPos,其指向这个子串的起始元素位置。
在遍历过程中,若发现即将加入set的新字符s[i]已经在set中存在,则stPos++,同时在set中移除s[stPos],直到 s[stPos] == s[i] 即找到了那个较早的字符。其他细节详见代码。
代码
class Solution {
public:
int lengthOfLongestSubstring(string s) {
if(s.size() == 0) return 0;
if(s.size() == 1) return 1;
set<char> st;
int ans = 1, stPos = 0;
for(int i = 0; i < s.size(); i++){
if(st.find(s[i]) == st.end()){
st.insert(s[i]);
}
else{
while(s[stPos] != s[i]){
st.erase(s[stPos]);
stPos++;
}
if(stPos != i){
stPos = stPos + 1;
}
}
ans = max(ans, (int)st.size());
}
return ans;
}
};

解法二 滑动窗口(官方题解)
检查了一下官方题解和我的区别,发现实际上官方题解是维护了子数组的end位置,对每一个可能的开头进行遍历。而我的想法是维护子串的开头位置,对每个可能的末尾进行遍历。
官方题解还有一个可以优化的地方(猜测这个地方就是比我慢的原因)官方题解每增加一项就必定对set有擦除和插入操作,但假如要插入的新元素就是上个起始位置的那个元素,其实没有必要对set访问
class Solution {
public:
int lengthOfLongestSubstring(string s) {
set<char> st;
int pos = 0;
int ans = 0;
for(int i = 0; i < s.size(); i++){
if(i) st.erase(s[i - 1]);
while(pos < s.size() && st.find(s[pos]) == st.end()){
st.insert(s[pos]);
pos++;
}
ans = max(ans, pos - i);
}
return ans;
}
};class Solution {
public:
int lengthOfLongestSubstring(string s) {
set<char> st;
int pos = 0;
int ans = 0;
for(int i = 0; i < s.size(); i++){
if(i) st.erase(s[i - 1]);
while(pos < s.size() && st.find(s[pos]) == st.end()){
st.insert(s[pos]);
pos++;
}
ans = max(ans, pos - i);
}
return ans;
}
};

解法三 记录每个字母的下标(哈希)
这是评论区找到的牛逼方法,速度是98%。
维护一个last数组,其中记录每个字母出现的最新位置,之后遍历字符串。维护start变量作为子串的起始长度,则对每一个新加进数组来的字符,都需要更新start为其上一次出现的位置加一。
具体实现见代码
class Solution {
public:
int lengthOfLongestSubstring(string s) {
vector<int> last(128, -1);
int ans = 0, start = 0;
for(int i = 0; i < s.size(); i++){
start = max(start, last[s[i]] + 1);
last[s[i]] = i;
ans = max(ans, i - start + 1);
}
return ans;
}
};


浙公网安备 33010602011771号