题目描述
找出字符串里不含重复字符的最长一段,返回它的长度。
思路解析
右指针 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)
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(字符集) —— 集合最多存下不同字符
套路模板
右指针只管往右扩,左指针在「不合法」时才收缩。几乎所有连续子串题都套它。
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))
以上文字与上方动画完全同一思路:先看动画建立直觉 → 读文字巩固 → 关掉页面自己默写一遍代码,能写出来才算真的掌握。