题目
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
示例 2:
输入: “bbbbb”
输出: 1
解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。
示例 3:
输入: “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。
思路
我知道,对某个问题想出最佳解法固然难得,然而要将自己的想法解释给他人听懂则更为难得。
对于这个问题,可以使用滑动窗口的方法来解决。
可以将字符串视为一条铁轨,而滑动窗口则是铁轨上的一辆列车。列车的长度可以伸缩,每节列车对应铁轨上的一个字符。
- 首先固定列车的尾部,将列车的头部向前延伸,每延伸一节,就查看新加的这一节对应的铁轨上的字符是否在列车中出现过,没出现过则继续延伸。
- 如果出现过,则将列车的长度-1就是到当前位置最大的无重复子串长度,记为maxlen。
- 固定列车的头部,将列车的尾部向前延伸,即缩短列车的长度,直到重复的情况消除。
- 重复步骤2-3,比较每次的maxlen,取较大者。直到列车头部到达铁轨终点,再取一次maxlen比较。
写代码
对于上述思路,在写代码的过程中可以进一步优化。思路在于,当出现重复时,我们将列车尾部逐渐前移,直到重复消除,如果能使尾部直接移到重复位置的下一个位置,则可以大大节约时间。
- 考虑使用一个hash-map,int m[128], 记录每个字符上一次出现的位置。并记录列车尾部的位置last.
- 列车头部前进时,每遇到一个字符c,就检查它上一次出现的位置m[c],如果m[c]>=last,则说明此字符在列车中出现过,更新maxlen,将l移至m[c]+1的位置,并更新m[c]为列车头部位置。
- 重复步骤2,直到字符串结尾,再更新一次maxlen。
附代码
/*
思路:记录子串的开头和结尾下标,并记录子串中每个字符的下标。
将结尾位置后移,如果新加入的字符上一次出现的下标大于开头,则说明此字符重复
将当前子串大小-1,并与result比较,取较大者
将开头移到上一次出现此字符的位置之后一位
继续后移结尾
*/
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int result=0;
vector<int> m(128,-1);//用于记录每个字符上一次出现的位置的下标
int size=s.size();
int f=0;//子串开头
int l=0;//子串结尾
//将l后移直到出现重复
while(l!=size)
{
if(m[s[l]]<f) m[s[l]]=l;//如果s[l]上一次出现的位置小于f,说明在当前子串中s[l]不重复
else//出现了重复,就把f向后移到消除重复的位置
{
//在此处更新一下result,f到l-1是不重复的
result=max(result,l-f);
f=m[s[l]]+1;
m[s[l]]=l;
}
++l;//后移l
}
//l到结尾退出,再更新一下result
result=max(result,l-f);
return result;
}
};