(c1)KMP算法:从记忆力到预知力
KMP:从记忆力到预知力
看完你能亲手推导next数组,并说清主串指针为何永不回退
按 空格/→ 演示下一步
全部页面点击任意一页,跳回舞台从这页播放
KMP:从记忆力到预知力
看完你能亲手推导next数组,并说清主串指针为何永不回退
暴力匹配怎么做
暴力匹配是KMP的起点:逐位对齐,失配就右移一位重来。
暴力匹配浪费在哪
绿色是已匹配成功、灰色是被丢弃的部分。跟着红色指针走一遍,看它怎样反复回退、反复重比。
已匹配串里藏着什么
上一页看到暴力匹配失配后把主串指针往回挪,白白重扫一遍。其实失配那一刻,主串和模式串刚比对过的那一段是完全已知的——已知的信息里,就藏着少走回头路的线索。
课文后半段与前半段相同,读到断点直接从熟悉处续背,不用重头
重复匹配的前缀
图示失配瞬间:已匹配部分的头尾有相同前缀,模式串直接滑到前缀对齐处,跳过重复比较。
next 数组:把重复记下来
用 Python 实算模式串 "ababc" 的 next 数组,看清每个位置的最长相等前后缀。
nxt[i] 记录「前 i+1 个字符」里,开头与结尾能重合的最长长度——正是失配时可以跳过的匹配量。
不变性:主串指针不回退
两法都逐位比较,最易混淆的是:失败时谁在动?主串指针 i 的行为是本质区别。
- i 是位置标记,随 j 重启
- 失配后 i 退回起点旁一位
- 已匹配字符被反复重扫
- i 是进度承诺,永不后退
- 失配后仅 j 跳到 next[j]
- 每个主串字符至多比较两次
记忆力:过去比较不白费
上一页我们定下规矩:主串指针绝不回退。那模式串失配后靠什么对齐?靠的是刚才已经匹配成功的那段——比较过的每个结果都记下来,失配时直接查记忆,而不是重新比一遍。
走过的路提前标记,回头不用重新探路
失配跳转一步步走
失配后四步走:查表、滑动、跳过、续比,主串指针始终不回退。
预知力:先看再比
上一页走跳转时,每失配一次才查一次 next 表。能不能更快?把主串拿来之前,所有失配位置的去向就先算好——模式串自己跟自己比,答案提前备好,比较时直接翻答案。
考场临时推导对应边比边想,提前背好对应先算好表
next 数组怎么算
next 数组不用暴力求:用模式串自己匹配自己,拿旧值推新值。
KMP 完整代码
把 next 预处理和主循环合在一起,逐行看记忆力和预知力怎么落到代码上。
两处 k = nxt[...-1] 是同一个动作:失配时不回头,而是查 next 表跳到下一个可能对齐的位置——这就是记忆变预知。
自测:失配跳到哪
KMP 匹配中主串指针 i 失配后,next 数组决定的是谁的动作?
要点收束
- ✓暴力匹配的浪费:失败后主串指针回退重比
- ✓已匹配部分的前后缀重叠,决定能跳过多少比较
- ✓next 数组把重叠信息预先算好,查表即跳
- ✓主串指针永不回退,比较次数 O(n+m)
- ✓next 的自匹配构造:KMP 用自己算自己
课后思考
先自己想五分钟,再看参考答案——答案只是思考的起点。
参考答案回顾核心不变性:已匹配前缀内部的信息已经由 next 数组记录,任何失配都能在「已匹配串内部」重新对齐,无需重读主串。若回退,最坏每趟都要重比已匹配部分,退回 O(n·m)。
参考答案模式串自重叠多(如 aaaab)、文本中频繁出现「部分匹配又失配」时优势最大——暴力每次都回退重比。若模式串无公共前后缀且很快失配,两者行为接近,差别不大。
参考答案能推广:对模式串预处理、失配时利用已比对信息的思路,催生了 Aho-Corasick(多模式)等。但编辑距离允许增删改、需要任意子串间的最优解,next 这种单链跳转不够,需 DP。