核心要点

  • 模板:右指针 right 不断扩张纳入新元素,破坏约束时左指针 left 收缩,直到窗口重新合法

  • 求最长:在窗口合法时更新答案;求最短:在窗口满足条件时更新答案再收缩

  • 复杂度:左右指针各最多移动 n 次,时间 O(n),用哈希表/计数器维护窗口状态

标准回答

滑动窗口本质是用左右两个指针维护一个动态区间,适合求满足某约束的连续子串/子数组的最长或最短长度。通用模板是:右指针每次纳入一个新元素,若此时违反约束(如出现重复、和超过目标),就移动左指针收缩窗口直到重新合法。区别在于更新答案的时机——求最长时在窗口合法的状态下记录长度,求最短时在窗口刚好满足条件时记录并继续收缩。下面以「无重复字符的最长子串」为例:

python
def length_of_longest_substring(s: str) -> int:
    # 求最长无重复字符子串:右扩纳入,遇重复则左缩
    last_seen = {}     # 字符 -> 最近一次出现的下标
    left = 0
    best = 0
    for right, ch in enumerate(s):
        # 若字符在窗口内出现过,左指针跳到重复位置的右侧
        if ch in last_seen and last_seen[ch] >= left:
            left = last_seen[ch] + 1
        last_seen[ch] = right
        # 当前窗口 [left, right] 内无重复,更新最长长度
        best = max(best, right - left + 1)
    return best


if __name__ == '__main__':
    print(length_of_longest_substring('abcabcbb'))  # 3 ('abc')
    print(length_of_longest_substring('bbbbb'))      # 1 ('b')
    print(length_of_longest_substring('pwwkew'))     # 3 ('wke')

常见误区

⚠️ 常见踩坑

更新 left 时要判断 last_seen[ch] >= left,否则窗口外的旧重复会把 left 错误地往回拉;只有 left 单调不减才能保证 O(n)。

追问

追问 1滑动窗口的时间复杂度为什么是 O(n) 而不是 O(n²)?

虽然有内外两层逻辑,但 left 和 right 都只单调向右移动,各自最多走 n 步,总移动次数 O(n)。哈希表查询为均摊 O(1),故整体 O(n)、空间 O(字符集大小)。

追问 2如果改成求「最短」满足条件的子串(如和 ≥ target),模板怎么变?

右指针扩张直到窗口满足条件,然后进入 while 循环不断收缩左指针并在每次满足时更新最短长度,收缩到不再满足为止。核心是「最长在合法时更新、最短在满足时收缩并更新」。

延伸学习

与本题相关的知识库文章、术语、工具与行业资讯。