·题目
LeetCode 209. 长度最小的子数组
LeetCode 3. 无重复字符的最长子串
LeetCode 76. 最小覆盖子串
LeetCode 134. 加油站
LeetCode 1234. 替换子串得到平衡字符串
LeetCode 992. K 个不同整数的子数组
LeetCode 395. 至少有 K 个重复字符的最长子串
·名字
滑动窗口
·地位
提升效率:通过动态调整窗口范围,避免暴力枚举所有可能的子区间,从而将时间复杂度从 O (N^2) 或更高优化到 O (N)
·用途
1.解决子数组和子串相关的问题
2.区域范围内最大值
·核心原理
窗口范围和答案要求有单调性关系(求最短的满足和为target的子串(全部>=0),那么范围越大和越大)
·大流程
子数组在以某个位置开头或结尾下的答案
·单调性
LeetCode 992. K 个不同整数的子数组
LeetCode 395. 至少有 K 个重复字符的最长子串
其中,这两题在学的时候对单调性产生了疑问,两者可以对比一下
疑问:严格等于k意味着没有单调性,可是窗口右边界右移时,“不同整数的数量” 可能增加,左边界右移时,数量又可能减少,为什么没有单调性?假设这个真的没有单调性,那395 我们要把字符种类数量规定为require是>=k的最长子串,那这里为什么是严格等于require?
单调性: 窗口扩张 / 缩小时,“目标条件是否满足” 的变化是单向、可预测的
问题一
方法一:直接把这个试
那么你就会发现,你不知道什么时候该把数字吐出来
nums = [1,2,1,2,3], k = 2
在[2,1], [1,2,1],[1,2,1,2]中,你不知道该不该缩小左边界
原因是什么? 不具有单调性!
这里你不论是吐数字还是吞数字,都会破坏严格等于k这个要求
所以往左缩会把有些答案丢了
方法二:对比解释(用豆包辅助)
对比 “有单调性” 和 “无单调性” 的场景
场景 1:“最多 k 个不同元素”(有单调性)
当目标是「子数组最多有 k 个不同元素」时:
右边界右移(扩张窗口):“不同元素数” 只会不变或增加 → 若增加到k+1,则不满足 “最多 k”;
左边界右移(收缩窗口):“不同元素数” 只会不变或减少 → 收缩后一定≤k。
此时,“是否满足条件” 的变化是单向的:右移扩张易 “破界”(从满足→不满足),左移收缩易 “回归”(从不满足→满足)。左右指针只需单向移动(右指针尽量右移,左指针仅在右指针导致破界时右移,且不会左移),符合单调性。
场景 2:“恰好 k 个不同元素”(无单调性)
当目标是「子数组恰好有 k 个不同元素」时:
右边界右移:“不同元素数” 可能从k→k+1(不满足 “恰好 k”);
左边界右移:“不同元素数” 可能从k→k-1(也不满足 “恰好 k”)。
此时,“是否满足条件” 的变化是来回波动的(满足→不满足→满足→不满足…),没有 “单向趋势”。左右指针无法 “一直朝一个方向滑”,必须来回调整(比如右移后不满足,左移又可能满足,再右移又不满足…),这就失去了滑动窗口的高效性(滑动窗口的优势是 O (n),若指针来回走,复杂度会退化)。
滑动窗口的 “单调性”,不是指 “数量的增减”,而是指 “条件满足与否的变化方向是单向的”
问题二
这里严格等于require滑动后的筛选,与窗口框架无关,框架是与<=require有关,所以具备单调性
综上所述
研究单调性,一定是要研究会影响答案的关键值的单调性,不要把筛选条件融为一体
提炼方法
当遇到严格等于为关键值的时候,转换为<=k