D

当前:LC76 · 最小覆盖子串 · 最短通关小队 · 首次出现于 Day 49 · 路径:顶栏「56天打卡」→ 点击 LC 题号 → 逐题动画

LC76

最小覆盖子串 · 最短通关小队

在队伍长廊 s 里找最短连续窗口,使 临时小队至少满足任务清单 t 字符频次(顺序无关)。

主示例 s

"ADOBECODEBANC"

任务清单 t

"ABC"

目标:最短窗口 = "BANC"(顺序 B-A-C,不是 ABC)

变长滑窗need 债务missing 计数O(n)

R 招人:右扩直到 missing==0(窗口 valid); L 裁员:在仍 valid 时尽量左缩,刷新最短纪录。看频次,不看顺序。

1/23 · 题意

候选队伍长廊 · s

窗口 [0, ]
× 不是 找连续 "ABC"  · × 不是 A→B→C 顺序  · ✓ 是 频次覆盖 (答案 BANC = B-A-C)
A
0
D
1
O
2
B
3
E
4
C
5
O
6
D
7
E
8
B
9
A
10
N
11
C
12
任务清单 t

A×1 B×1 C×1

当前临时小队

(空)

任务:最短通关小队

s 是候选队伍长廊,t 是任务清单。要在 s 里找一段最短连续窗口,使窗口内至少包含 t 要求的每一种字符、且数量够。

s = "ADOBECODEBANC",t = "ABC" → 需要 A×1、B×1、C×1。目标:最短连续子串覆盖全部任务。

为什么这样做

这是「最小覆盖子串」:覆盖看频次,不看顺序。

代码同步提示

先统计 t 里每个字符还要多少个 → need 表;missing = 还欠几个字符实例

Go · minWindow
1func minWindow(s string, t string) string {
2 if len(t) == 0 || len(s) == 0 || len(t) > len(s) {
3 return ""
4 }
5 need := make(map[byte]int)
6 for i := 0; i < len(t); i++ {
7 need[t[i]]++
8 }
9 missing := len(t)
10 left := 0
11 start, minLen := 0, len(s)+1
12
13 for right := 0; right < len(s); right++ {
14 c := s[right]
15 if need[c] > 0 {
16 missing--
17 }
18 need[c]--
19
20 for missing == 0 {
21 if right-left+1 < minLen {
22 start = left
23 minLen = right - left + 1
24 }
25 lc := s[left]
26 need[lc]++
27 if need[lc] > 0 {
28 missing++
29 }
30 left++
31 }
32 }
33 if minLen == len(s)+1 {
34 return ""
35 }
36 return s[start : start+minLen]
37}

行级中文解释

  • L14, L15

    need[c] > 0 表示这个字符还欠着,读到它可以还债,missing--

  • L16

    need[c]-- 表示字符进入窗口;哪怕是多余的也要记录(会变成负数)

  • L18

    missing == 0 表示窗口已经覆盖 t 的全部频次

  • L24, L25

    收缩左边时 need[lc]++;若变回正数,说明移走了必要实例,窗口失效

  • L25, L26

    need[lc] > 0 时 missing++,停止继续收缩

常见误区

  • 只比「有没有」不比「有几个」

    t="AAB" 需要 A×2;窗口 "AAAB" 里虽有 A、B 两种字符,但 A 只出现 1 次仍不 valid。

  • 误以为要找连续子串 "ABC"

    答案 "BANC" 的顺序是 B-A-C;覆盖看频次,不看排列顺序。

  • valid 后立刻停

    第一次 valid 往往很长;要在 valid 后继续缩 L,才能拿到最短窗口。

步骤一览

面试口述模板

我会用变长滑动窗口。先用 need 记录 t 中每个字符还需要多少个,missing 表示还差多少个字符实例。右指针不断扩张窗口,每读到一个还欠着的字符,就让 missing--。当 missing==0 时,说明窗口已经覆盖 t,此时不断移动左指针收缩窗口,并更新最短答案。如果移走某个字符导致 need 变回正数,说明窗口失效,停止收缩。每个字符最多进窗口一次、出窗口一次,所以时间复杂度 O(n)。

迁移练习

  • LC3 无重复最长子串(变长滑窗,但不变量是「无重复」)
  • LC438 找所有异位词(定长滑窗 + 频次匹配)
  • LC567 字符串排列(同 LC438 思路)