起初的想法是通过一个滑动窗口,不停的前进,然后查看下一个字符是否在窗口内,然后判断长度是否是最长的。
但是该方法复杂度较高,后来在阅读提示和解答后,发现在最长只有可能出现在两个重复字符之间,可以过滤大部分的字符,得到的代码如下:
class Solution {
public int lengthOfLongestSubstring(String s) {
int len = s.length();
Map<Character, Integer> map = new HashMap<>();
int max = 0;
for(int i=0,j=0;i<len;i++){
if(map.containsKey(s.charAt(i))){
j = map.get(s.charAt(i))+1;
}
max = max < i-j+1 ? i-j+1 : max;
map.put(s.charAt(i), i);
}
return max;
}
}
复制代码
运行后发现对于tmmzuxt这种字符串一直无法成功,想了很久没有想明白,最后单步调试发现,当执行到最后一个t的时候,起始位置一下子移动到了第一个t后面的位置,后修改代码如下
class Solution {
public int lengthOfLongestSubstring(String s) {
int len = s.length();
Map<Character, Integer> map = new HashMap<>();
int max = 0;
for(int i=0,j=0;i<len;i++){
if(map.containsKey(s.charAt(i))){
//该步骤有两个作用:
//1.将起始位置j从重复的位置往后移动
//2.防止起始位置回到最开始的位置,起始位置只能向后递增,不能回溯
j = Math.max()map.get(s.charAt(i))+1,j);
}
max = max < i-j+1 ? i-j+1 : max;
map.put(s.charAt(i), i);
}
return max;
}
}
复制代码
其实该方法的主题思路是:
一开始的理解是最长不重复子串必然是在两个重复的字符之间,所以就没有考虑到起始位置前移的问题;
后来发现,起始位置非常重要,最长的重复子串要么出现在起始位置之前,要么出现在起始位置之后,之前的话已经计算过了最长的子串,之后的话还是要继续计算。