滑动窗口解题框架
本节目标
用左右边界维护连续区间状态,统一处理固定长度、最长合法和最短可行子数组或子串。
滑动窗口把枚举所有连续区间改成一次从左到右的扫描。右边界负责把新元素纳入状态,左边界只在窗口违反约束或已经足够好时前进;每个元素最多进、出窗口一次。
识别信号
优先考虑滑动窗口的题目通常同时满足:
- 对象是连续子数组或子串;
- 题目要求最长、最短、计数或每个固定长度区间的答案;
- 区间状态能在加入右端元素、移除左端元素时增量维护;
- 不需要回头修改已经移出的元素。
例如无重复、包含所有需要字符、和至少为目标值和每个固定长度区间最大值,都在维护一个连续区间的状态。
问题模型与核心不变量
本章把当前窗口写作闭区间 [left, right]。每轮先把右端元素加入状态,再按题意决定是否移动左端。
- 固定长度窗口:每次比较前,窗口长度恰为目标长度。
- 最长合法窗口:收缩后窗口始终合法,记录其最大长度。
- 最短可行窗口:一旦窗口可行就持续收缩,记录可行窗口的最小长度。
窗口状态应只保存判断题目条件所必需的信息,例如字符频次、元素和,或单调队列中的候选下标。
通用模板
left = 0 state = 空状态 for right 从 0 到 n - 1: 把输入[right] 加入 state 当窗口需要收缩: 用当前窗口更新答案 从 state 移除输入[left] left 向右移动 若窗口正好满足固定长度: 用当前窗口更新答案
关键是先回答两个问题:什么条件触发收缩,以及在收缩前还是收缩后记录答案。最长合法窗口在恢复合法后记录;最短可行窗口在仍然可行时先记录再收缩。
模板变体
固定长度
窗口长度始终不超过 k。右端加入后,若长度超过 k 就移出左端;长度等于 k 时得到一个答案。字符异位词和窗口最大值都属于这一类,但后者需要单调队列快速取得最大值。
最长合法窗口
先扩张右边界;只要违反约束,就不断移出左端,直到窗口重新合法。之后用窗口长度更新最大答案。无重复字符的最长子串中,约束就是任意字符频次不超过 1。
最短可行窗口
先扩张到可行,再尽量收缩。由于收缩会让窗口更短,所以每次收缩前都可能得到新的最优答案。长度最小的子数组要求所有数字为正;最小覆盖子串则用频次和满足种类数判断可行性。
母题序列
按必学顺序练习三题,分别对应最长合法、固定长度和最短可行窗口:
- 无重复字符的最长子串 必学:频次超过 1 就收缩,维护最长合法区间。
- 找到字符串中所有字母异位词 必学:维护固定长度字符计数,并比较两个频次数组。
- 长度最小的子数组 必学:和达到目标后持续收缩,寻找最短可行区间。
常见误区
- 把窗口合法和窗口可行混为一谈:最长题要保持合法,最短题要在可行时收缩。
- 固定长度窗口忘记在长度超过目标时先移除左端,导致比较了错误长度。
- 最短和问题在数组含负数时仍套用正数数组模板;此时左移不再保证总和单调变化。
- 只记录某个字符是否出现,却没有保存重复需求的次数,无法处理 AABC 这类目标。
- 单调队列保存值而不保存下标,无法判断队首是否已经离开窗口。
迁移方向
基础频次与区间和窗口可以继续迁移到两类综合题:
若问题不要求连续区间,或状态不能随着左右端点移动而增量更新,应改用前缀信息、哈希表或其他框架。