练习地址:力扣
思路解析:
方法一:考虑暴力的做法,对于每一个字符串利用双指针扫描,每扫描一次利用map存储一次当前字符出现的次数,如果发现有一个字符在之前出现过,则 i 指针向后移动一位,继续令 j=i+1重新扫描,时间复杂度O(N^2) .
方法二:利用滑动窗口优化.思路是这样的,假设有一个字符串bcadeafg,我们将 j 和 i 指针一开始都置于0位置,接着i指针不动, i 指针像暴力做法一样向下扫描,每扫描一个都一样将当前扫描到的字符出现的次数存放在hash表中,也就是h[s[ i ] ]++的操作,当扫描完bcade的时候一切正常这时候 i 指针指向了a,发现a在 j ~ i 的子串之间出现了两次,所以出现了重复这时候我们将 j 指针移动到a的位置,并将沿路的字符出现的次数减去1,对于新位置再次重复以上操作,其中需要记录(i-j+1)也就是每次发现出现字符重复时候的子串长度,进行更新.
证明:现在来解释一下为什么 j 指针可以直接移动到第一个重复的字符的位置,假设不是 j 移动到a停止而是移动到c停止,那么仍然存在重复字符,并且由于 i 和 j之间包含了重复字符,所以即使 j 只向前移动一个位置,i 如果暴力回来重新循环的话,i再次到达第二个a的时候最长的子串长度肯定不会超过j位于b时候的 ,因为少了一个字符的长度,所以j位于第一个重复字符的前面的比较毫无意义,只需要记录一下位于最开始位置的那个最长不重复子串就行了.所以干脆直接把 j 移动到第一个重复a的位置重新开始比较.
c++实现:
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int res=0; //存储结果
unordered_map<char,int>h; //定义哈希表,记录某一个字符在j~i之间出现的次数
for(int i=0,j=0;i<s.size();i++)
{
h[s[i]]++; //首先将当前扫描到的字符个数加一
while(h[s[i]]>1) h[s[j++]]--; //如果发现当前字符重复了,则j往前移动将沿途的字符出现次数减一,直到遇到重复字符当中的第一个
res=max(res,i-j+1); //对比每次i指针遇到重复字符时候的长度,选取最大的长度记录结果
}
return res;
}
};