题目描述
思路解析动画文字版
把每个起点、每个终点都试一遍当然能做,但 n² 个子串里绝大多数明显不够格,白白检查,太慢。
用 need 记下 t 里每个字母还差几个。右指针 r 不停吃字符,直到窗口把 t 全包住;这时再移左指针 l 把多余的左边挤掉,窗口能缩就缩、缩到不能再缩,就拿到一个最短候选。右扩只会让窗口更可能覆盖,左缩只在已覆盖时做——所以两根指针都不回头。
准备:先数 t="BC":need = {B:1, C:1},一共要凑齐 2 种字母才算覆盖。窗口从空开始。
r = 0 · 吃进 A:A 不是 t 需要的字母,吃进窗口但 need 不变,仍差 B、C 两种。
r = 1 · 吃进 D:D 同样不需要。窗口 "AD" 还是没覆盖 t——这就是为什么不能急着检查、要让 r 继续扩。
r = 2 · 吃进 B:B 正是要的!need[B] 从 1 减到 0,凑齐种类数 +1。现在差 C 一种。
r = 3 · 吃进 C:C 也吃进来,need 全部归零,凑齐数 = required = 2,第一次覆盖 t!记下候选 "ADBC"(长 4)。覆盖之后,轮到左指针登场——开始收缩。
r = 3 · 左缩① 丢 A:最左是 A,不是 t 要的,丢掉它窗口照样覆盖。l 前进到 1,窗口缩成 "DBC"(长 3),刷新最短。
r = 3 · 左缩② 丢 D:D 同样多余,丢掉,l 到 2。窗口缩成 "BC"(长 2)——更短了!继续试着缩。
r = 3 · 左缩③ 想丢 B:再想丢最左的 B——但 B 是 t 必需的!一旦丢掉,need[B] 弹回 1,覆盖被打破。这就是收缩的边界:缩到「再缩一步就不覆盖」就得停。所以 "BC" 是这一轮的最短。
结束:r 已到末尾、左边也缩到极限。整个过程里最短的覆盖子串是 "BC",就是答案。
「找最短的合法窗口」都是这个手感:r 扩到刚好合法,l 缩到再缩就不合法。判覆盖靠 formed == required,别去重排或重数整个窗口。
迁移阶梯:把这题练到能复述后,再去同类题里迁移:先复用状态定义,再调整边界条件。
边界三连:边界三连:极端输入先过一遍。
几个高频追问,记住答法。
参考代码
from collections import Counterclass Solution: def minWindow(self, s, t): if not s or not t: return "" need = Counter(t) # 每种字符还需要几个 required = len(need) # 要凑齐的「字符种类数」 formed = 0 # 窗口里已凑齐的种类数 window = {} l = 0 best_len, best_l = len(s) + 1, 0 for r, ch in enumerate(s): window[ch] = window.get(ch, 0) + 1 if ch in need and window[ch] == need[ch]: formed += 1 # 这一类刚好凑齐 while formed == required: # 已覆盖 → 试着左缩 if r - l + 1 < best_len: best_len, best_l = r - l + 1, l left = s[l] window[left] -= 1 if left in need and window[left] < need[left]: formed -= 1 # 缩掉后这一类不够了 l += 1 return "" if best_len == len(s) + 1 else s[best_l:best_l + best_len]复杂度
- 时间复杂度:O(n),l 和 r 各自只向右走一遍,每个字符进窗口、出窗口各一次
- 空间复杂度:O(字符集),need 最多存下 t 里的不同字母
可套用的代码模板
右指针只管扩;一旦合法就进 while 不停左缩、边缩边记最短,缩到不合法才退出。求「最短」就在 while 里记答案,求「最长」则在 while 外记。
Python
# 变长滑窗求「最短合法窗口」通用骨架l = 0for r in range(len(s)): 把 s[r] 加入窗口(更新计数 / formed) while 窗口已合法(formed == required): # 是 while 不是 if 更新最短答案 (r - l + 1) 把 s[l] 移出窗口;若某类不再够则 formed -= 1 l += 1# 求「最长」则把记答案挪到 while 外易错点
面试追问把动画讲成自己的话
追问怎么 O(n) 判断覆盖?
追问为什么左右指针都只前进、就是 O(n)?
追问t 有重复字符怎么算?
这道题到这就讲完了。动画和文字是同一套思路——别光看,关掉页面自己默写一遍。然后顺着主线继续:
滑动窗口最大值
LeetCode 239 · 困难 · 沿着 滑动窗口 继续往下推进
把这道题真正学会,再走
图解算法年卡 ¥99 /年
- ✓本题每步动画的吴师兄语音讲解(全站陆续覆盖)
- ✓小欧带 8 步通关训练——追问到不看答案也能写对、能 30 秒讲给面试官听
- ✓学习报告,记录每道题的掌握程度
76k+ GitHub Star · 吴师兄开源图解算法,几十万开发者在看的算法讲解
想成体系刷透这类套路?去图解算法专题