一、题目简介
题目地址是:https://leetcode.com/problems/longest-substring-without-repeating-characters/
题目内容:输入一个字符串例如"abcabcbb",找到最长的不重复的字符串的长度返回,这里应该返回的是"abc"的长度3
二、解题思路简述
该题的关键是设计一个"滑动窗口",里面存的是最长的不重复字符串,一开始这个窗口是空的,如下图,绿色代表滑动窗口,蓝色的是外部输入的字符串:
从字符串的第一个元素开始,执行这个逻辑:
a. 用一个变量保存当前已经发现的最大长度,假设名为maxLen;
b. 如果当前检查的元素在窗口中没有,就加入窗口,例如窗口中已经有了"ab",当前是"c",那么窗口中最大长度加一;
c. 如果当前检查的元素在窗口中存在,例如窗口中已经有了"abc",当前检查的元素是"a",这个"a"在窗口总已经存在了,窗口作调整,窗口中的"a"和它左侧的所有元素全部移除窗口,再把当前检查的"a"元素加入窗口;
d. 拿当前窗口的长度和maxLen比较,大的值存入maxLen中;
e. 继续检查字符串的下一个元素,逻辑是前面的步骤;
三、思路详细图解
以前面的"abcabcbb"为例,来把上述逻辑用图片演示一遍;
1、检查完第一个元素后,窗口效果如下图,可见第一个元素已经纳入窗口中:
2、检查完第三个元素后,效果如下图,maxLen等于3,三个元素纳入窗口:
3、检查到第四个元素是"a",那么窗口的左侧和右侧都开始滑动,maxLen等于3,当前窗口中的长度也是3,因此maxLen不变,如下图:
4、继续检查第5个元素,这次遇到的"b"在窗口中也存在,处理如下图:
5、继续检查第6个元素,这次遇到的"c"在窗口中也存在,所以第一个"c"移出窗口,第二个"c"加入窗口,窗口内的元素还是3个,所以maxLen保持不变,处理如下图:
6、检查第7个元素"b",窗口中已经有"b"了,所以左侧窗口向右收缩,将里面原有的中的"b"移出,右侧窗口向右扩张,将第7个元素"b"纳入窗口中,此时窗口中元素数量为2,小于maxLen,所以maxLen不变,如下图:
检查第8个元素"b",发现窗口中已经有"b"了,只能将窗口中的"b"(第7个元素)和它左侧的"c"(第6个元素)都从窗口中移出,再把第8个元素纳入窗口,此时窗口中只有一个元素,因此maxLen不变,如下图:
数组已经遍历完毕,返回maxLen=3,解题结束;
四、代码示例
import java.util.HashSet;
import java.util.Set;
public class Solution2 {
public static void main(String[] args) {
System.out.println((new Solution2()).lengOfLongestSubstring("abcabccbb"));
}
public int lengthOfLongestSubstring(String s) {
//窗口的起始位置,窗口的结束为止,最长记录
int left = 0, right = 0, max = 0;
//表示窗口内有哪些值
Set<Character> set = new HashSet<>();
while (right < s.length()) {
//例如"abcdc",窗口内是"abcd",此时right等于[4],
//发现窗口内有array[right]的值,就缩减窗口左边,
//缩到窗内没有array[right]的值为止,
//当left一路变大,直到left=3的时候,窗口内已经没有array[right]的值了
if (set.contains(s.charAt(right))) {
//假如窗口内是"abc",当前是"c",那么下面的代码只会将"a"删除,left加一,再次循环
//而新一次循环依旧发现"c"还在set中,就再把"b"删除,left再加一...
set.remove(s.charAt(left++));
} else {
//窗口内没有array[right]的时候,就把array[right]的值放入set中,表示当前窗口内有哪些值
set.add(s.charAt(right++));
if ((right - left) > max) {
max = right - left;
}
}
}
return max;
}
}
上述代码的关键是set.remove(s.charAt(left++)),配合着外面的while循环,"left++"表示将窗口向右移动一个元素,并且将窗口中最左侧的元素从set中删除。