滑动窗口:
本文章介绍的滑动窗口并非网络领域中的滑动窗口技术,而是编程实践中的一种算法思想。
简单来说它就是一种常用来处理字符串或者数组的连续子元素的一些操作的算法。这个思想可以大量减少冗余操作,一般来说滑动窗口解决问题时间复杂度更低。(在本文后面我们将通过对例题图解来了解该算法思想)
题目:无重复字符的最长子串
来源:力扣(LeetCode)
链接:无重复字符的最长子串
描述:给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例1:
输入: "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例2:
输入: "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例3:
输入: "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
分析:
本题是一个典型的可以通过滑动窗口思想解决的问题。
我们来一步步分析:
如果我们能列举出所有的子串再进行一次筛选比较就能得到想要的结果,但是随着输入字符串的长度增大,其子串会快速增大,以至于处理较长字符串就会内存溢出。
再来看上一步中我们提到的所有子串,不难发现其实很明显有些子串是不可能最大的,比如长度大于2的有两个或两个以上不同字符的字符串的最长子串显然不可能是1。那么如何给程序设定规则就显得很重要。我们是采用通过两个标记(类似于指针),标记开始一个字符串开始位置,和结束位置,每次获得的子串为开始标签和结束标签之间的内容。
我们设置外循环次数为输入字符串的长度,内循环的次数是当前 i 到字符串尾的长度。
编写代码让标签移动规则如下:
- 每次循环开始标签和结束标签都在本次循环 i 的位置,内置循环当结束标签到字符串末尾则当次外循环结束。
- 内循环每次加一则结束标签向后移一位。
- 当开始标签到字符串末尾则外循环结束。
图解:
在下图中每一行是一次外循环,每一列是一次内循环,粗下划线标识的内容为每次获得的子串。
代码:
public static int lengthOfLongestSubstring(String s) {
if(s==null) return 0;
int maxl=0;
for(int i=0;i<s.length();i++){
Map<Character,Boolean> m=new HashMap<>();
char left=s.charAt(i);
String tps=""+left;
m.put(left,true);
for(int j=i+1;j<s.length();j++){
char mid=s.charAt(j);
if(m.containsKey(mid)){
break;
}else{
m.put(mid,true);
tps+=mid;
}
}
maxl=Math.max(tps.length(),maxl);
}
return maxl;
}