跳到主要内容

滑动窗口解题框架

本节目标

用左右边界维护连续区间状态,统一处理固定长度、最长合法和最短可行子数组或子串。

滑动窗口把枚举所有连续区间改成一次从左到右的扫描。右边界负责把新元素纳入状态,左边界只在窗口违反约束或已经足够好时前进;每个元素最多进、出窗口一次。

识别信号

优先考虑滑动窗口的题目通常同时满足:

  • 对象是连续子数组或子串;
  • 题目要求最长、最短、计数或每个固定长度区间的答案;
  • 区间状态能在加入右端元素、移除左端元素时增量维护;
  • 不需要回头修改已经移出的元素。

例如无重复、包含所有需要字符、和至少为目标值和每个固定长度区间最大值,都在维护一个连续区间的状态。

问题模型与核心不变量

本章把当前窗口写作闭区间 [left, right]。每轮先把右端元素加入状态,再按题意决定是否移动左端。

  • 固定长度窗口:每次比较前,窗口长度恰为目标长度。
  • 最长合法窗口:收缩后窗口始终合法,记录其最大长度。
  • 最短可行窗口:一旦窗口可行就持续收缩,记录可行窗口的最小长度。

窗口状态应只保存判断题目条件所必需的信息,例如字符频次、元素和,或单调队列中的候选下标。

通用模板

left = 0 state = 空状态 for right 从 0 到 n - 1: 把输入[right] 加入 state 当窗口需要收缩: 用当前窗口更新答案 从 state 移除输入[left] left 向右移动 若窗口正好满足固定长度: 用当前窗口更新答案

关键是先回答两个问题:什么条件触发收缩,以及在收缩前还是收缩后记录答案。最长合法窗口在恢复合法后记录;最短可行窗口在仍然可行时先记录再收缩。

模板变体

固定长度

窗口长度始终不超过 k。右端加入后,若长度超过 k 就移出左端;长度等于 k 时得到一个答案。字符异位词和窗口最大值都属于这一类,但后者需要单调队列快速取得最大值。

最长合法窗口

先扩张右边界;只要违反约束,就不断移出左端,直到窗口重新合法。之后用窗口长度更新最大答案。无重复字符的最长子串中,约束就是任意字符频次不超过 1。

最短可行窗口

先扩张到可行,再尽量收缩。由于收缩会让窗口更短,所以每次收缩前都可能得到新的最优答案。长度最小的子数组要求所有数字为正;最小覆盖子串则用频次和满足种类数判断可行性。

母题序列

必学顺序练习三题,分别对应最长合法、固定长度和最短可行窗口:

  1. 无重复字符的最长子串 必学:频次超过 1 就收缩,维护最长合法区间。
  2. 找到字符串中所有字母异位词 必学:维护固定长度字符计数,并比较两个频次数组。
  3. 长度最小的子数组 必学:和达到目标后持续收缩,寻找最短可行区间。

常见误区

  • 把窗口合法和窗口可行混为一谈:最长题要保持合法,最短题要在可行时收缩。
  • 固定长度窗口忘记在长度超过目标时先移除左端,导致比较了错误长度。
  • 最短和问题在数组含负数时仍套用正数数组模板;此时左移不再保证总和单调变化。
  • 只记录某个字符是否出现,却没有保存重复需求的次数,无法处理 AABC 这类目标。
  • 单调队列保存值而不保存下标,无法判断队首是否已经离开窗口。

迁移方向

基础频次与区间和窗口可以继续迁移到两类综合题:

若问题不要求连续区间,或状态不能随着左右端点移动而增量更新,应改用前缀信息、哈希表或其他框架。