滑动窗口
这一题是leetcode的NO.3题,曾经被面到过,很经典的一道题
注意:子串与子序列的区别
子串:不可跳跃,如
pwwkew中wke是子串子序列:可以跳跃,如
pwwkew中pkw是子序列
方法1:粗糙版滑动窗口
用
Set来维护重复字符与否的问题出现新的字符,很好,
right指针向右扩展,并将当前字符加入Set中,更新最长无重复子串长度出现旧的字符,将左边界
left指针向右收缩,移除当前的left指针指向的字符
public int lengthOfLongestSubstring1st(String s) {
int res = 0, left = 0, right = 0;
int n = s.length();
Set<Character> set = new HashSet<>();
while (right < n && left < n) {
if (!set.contains(s.charAt(right))) {
set.add(s.charAt(right++));
res = Math.max(res, right - left);
} else {
set.remove(s.charAt(left++));
}
}
return res;
}方法2:优化版滑动窗口
用一个
hashmap来建立字符和其出现位置之间的映射。维护一个滑动窗口,窗口内的都是没有重复的字符,去尽可能的扩大窗口的大小,窗口不停的向右滑动。
(1)如果当前遍历到的字符从未出现过,那么直接扩大右边界;
(2)如果当前遍历到的字符出现过,则缩小窗口(左边索引向右移动),然后继续观察当前遍历到的字符;
(3)重复(1)(2),直到左边索引无法再移动;
(4)维护一个结果
res,每次用出现过的窗口大小来更新结果res,最后返回res获取结果。用一个
mapper记录出现过并且没有被删除的字符用一个滑动窗口记录当前
index开始的最大的不重复的字符序列用
res去记录目前位置最大的长度,每次滑动窗口更新就去决定是否需要更新res
方法3:再优化版滑动窗口
常用的表如下所示:
准备一个
helper数组,每次记录right指针的绝对位置,更新
res,更新left(拿到其最大位置)
方法1:双端队列

方法2:双端队列
方法3:辅助数组+贪心
国际站看的的一个思路,很赞

如上图:
1.将源数组按k的大小分,最后一组不够k的话,维持现状
2.1.遍历数组,组装left_max,即从左开始,每个k组从左开始,取最大值
2.2.遍历数组,组装right_max,即从右开始,每个k组从右开始,取最大值
3.借助左右辅助数组拼装结果数组,Math.max(right_max[i], left_max[i + k - 1])
实现
上述贪心的证明:
思路来源国际站,很赞的一个证明
假设$a_0$,$a_1$,$a_2$ ... $a_n$的窗口宽度是$w$,目标是为了获取一个$d[]=int[n-w+1]$
将上面的数组从左边开始分,每个元素的形式:$i*w+j$,其中$i$是从左边开始数,窗口的$index$,$j$是在这个窗口下的偏移量,其中$0<=i<=n/w$, $0<=j<=w$
构建如下的结果:
$d[iw+j]=max(a[iw+j+x])$这里的$x$满足:$0<=x<=w$,因此,$i*w+j$实际上代表的是要计算的最大值
假设有如下的数组:
$left[iw+j]=left_max(a[iw+k])$ 其中$0<=k<j$
$right[iw+j]=right_max(a[iw+k])$ 其中$j<=k<=(i+1)*w-1$
数组$left[]$是从左到右的每个窗口最大值
数组$right[]$是从右到左的每个窗口的最大值

有如下的推导: $d[iw+j]=max(right[iw+j], left[(i+1)w+j-1])$ $d[iw+j]=max(right[iw+j], left[(iw+w+j-1])$ => $d[m] = max(right[m], left[m+w-1])$
结果数组$d[]$的最后一个元素是:$d[n-w]=max(right[n-w], left[n-1])$
159.最多有两个不同字符的最长子串
方法1:Map统计数量+滑动窗口
方法2:Map标记位置+滑动窗口
方法3:滑动窗口+指针
340.最多有K个不同字符的最长子串
方法1:Map统计数量+滑动窗口
方法2:Map标记位置+滑动窗口
方法1:递归
拿掉边界的处理
方法1:滑动窗口


另外一种思路:
方法2:滑动窗口+统计
思路来自@lee215大佬,该解法可以参考424题
Follow up:返回所有符合条件的索引
Last updated