题目是这样子的
给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。
这道题主要使用到的思路是滑动窗口,每当我们选择一个字符作为查询的开始之后,每次搜到的子串都要判断是否存在重复的字符,如果存在,那么当前开始的字符的最长子串就是确定的。
打个比方,测试用例中的abcabcbb,当我们选择第一个字符a作为开始,我们会筛选到以下子串
- ab
- abc
- abca
当我们在搜到第三个子串时,发现当前字符a已经存在了,则可以确定第一个字符a的最长子串就是子串2。这个就是滑动窗口的设计,需要两个指针left和right,其中每次搜索时left固定,然后向右移动right,只要出现重复的字符,就修改left的位置。
这里还有一个设计,就是关于当前搜索的子串存储。可以用一个Map<Character,Integer>用来存储当前的子串,每次遇到重复的字符,都要把上一个字符的位置删掉,也就是把当前left指定的字符去掉,换成当前重复所在的位置。
整理之后的代码就是
class Solution {public int lengthOfLongestSubstring(String s) {if (s.length() == 0) {return 0;}if (s.length() == 1) {return 1;}char[] cs = s.toCharArray();Map<Character, Integer> map = new HashMap<>();int left = 0;int max = 0;// left固定从0开始,而right指针则可以从第二个字符开始for (int right = left ; right < cs.length; right ++) {// 如果当前right对应的字符在map中重复出现,需要判断是否在窗口里面// 例如FABCDFCBGH,如果此时left的位置是A,而right的位置是第二个F// 那么上一次出现F的时候不在窗口里面,不影响A的最长子串获取// 但是如果left是B,而right指向了第二个C,而C第一次出现在窗口里面,// 则确定了B的最长子串,就需要移动left到C第一次出现的位置的下一个字符// 为啥是由重复字符确定left的指针位置呢,因为在B到C第一次出现之前的位置都是筛选过的,// B和C第一出现的中间字符最大也不可能超过B开始的子串,所以得从C第一次出现的下个字符开始// right是当前搜索的字符,所以不用 <= if (map.containsKey(cs[right]) && map.get(cs[right]) >= left && map.get(cs[right]) < right) {left = map.get(cs[right]) + 1;}// 把当前字符和位置塞入Map中,这样也可以实时更新字符的位置,顺便把重复字符的位置更新map.put(cs[right], right);// max表示最大子串的长度max = Math.max(max, right - left + 1);}return max;}
}