图解题库 / 滑动窗口

3. 无重复字符的最长子串

中等 含交互动画

学完这道你应该能: 说清它为什么用「滑动窗口」,而不是死记步骤; 合上页面,自己默写出核心代码; 用 30 秒把思路讲清楚。

题目描述

找出字符串里不含重复字符的最长一段,返回它的长度。

s = "pwwkew"
输出 = 3 (最长是 "wke")

思路解析

右指针 r 一直往右把新字符吃进窗口;一旦窗口里出现重复,就移动左指针 l 把左边挤出去,直到不再重复。窗口里随时都是「无重复的一段」。

准备:用一个集合记下「窗口里现在有哪些字符」,方便判断有没有重复。

r = 0 · 吃进 p:p 不在窗口里,放心吃进来。窗口 = "p",当前最长 = 1。

r = 1 · 吃进 w:w 也不在窗口里,吃进来。窗口 = "pw",最长更新为 2。

r = 2 · 又遇到 w:想吃进 2 号位的 w,可窗口里已经有 w 了——重复!这时不能直接放,得先收缩左边。

r = 2 · 收缩①:把窗口最左边的 p 移出去,l 前进到 1。但窗口里 1 号位还是 w,仍然和要吃的 w 重复,继续收缩。

r = 2 · 收缩②:再把 1 号位的 w 也移出去,l 到 2。现在窗口空了,不再有 w 了。

r = 2 · 吃进 w:现在可以把这个 w 吃进来了。窗口 = "w",长度 1,没超过之前的最长 2。

r = 3 · 吃进 k:k 不在窗口,吃进来。窗口 = "wk"。

r = 4 · 吃进 e:e 不在窗口,吃进来。窗口 = "wke",长度 3,刷新最长记录!

r = 5 · 又遇到 w:又想吃 w,但 2 号位有 w,重复。收缩左边。

r = 5 · 收缩:把 2 号位的 w 移出,l 到 3。窗口里没有 w 了。

r = 5 · 吃进 w:吃进 w,窗口 = "kew",长度 3,没超过最长。

结束:r 走到头。整个过程中窗口最长是 3("wke"),这就是答案。

滑动窗口能套很多题:最小覆盖子串、长度最小子数组、字母异位词……记住「窗口伸缩」这个手感。

参考代码(Python)

Python
1def lengthOfLongestSubstring(self, s):
2    maxLen = 0
3    hash = set()                  # 窗口里的字符
4    start = 0                     # 左指针 l
5    for end in range(len(s)):     # 右指针 r 不断右扩
6        while s[end] in hash:     # 出现重复,收缩左边
7            hash.remove(s[start])
8            start += 1
9        hash.add(s[end])          # 吃进新字符
10        maxLen = max(maxLen, end - start + 1)
11    return maxLen

复杂度分析

  • 时间复杂度:O(n) —— l 和 r 各自只向右走一遍
  • 空间复杂度:O(字符集) —— 集合最多存下不同字符

套路模板

右指针只管往右扩,左指针在「不合法」时才收缩。几乎所有连续子串题都套它。

Python
1# 连续子串/子数组通用骨架
2l = 0
3for r in range(len(s)):
4    加入 s[r] 到窗口
5    while 窗口不合法:
6        移除 s[l]; l += 1
7    更新答案(r - l + 1)

易错点

  • 收缩时忘了把 s[l] 移出哈希/计数 → ✅ l 右移的同时同步移除 s[l](否则窗口状态记错、结果偏大)
  • 用 if 判断收缩 → ✅ 用 while 一直缩到合法(一次可能要移除多个(如 pwwkew 的两个 w))

以上文字与上方动画完全同一思路:先看动画建立直觉 → 读文字巩固 → 关掉页面自己默写一遍代码,能写出来才算真的掌握

下一题 →46. 全排列 ← 返回题库