Skip to content

算法Algorithm

编辑距离的网格动态规划

Levenshtein distance dynamic programming · Wagner–Fischer algorithm

把插入、删除与替换写成前缀网格的三类边,求出最小改写费用并重建逐条可执行的脚本。

形式陈述 ​

两个版本的文字不同,差异可能来自换字,也可能来自插入一个字以后整体错位。怎样同时允许这两种变化,并找到费用最小的改写过程?

设 A=a1⋯am、B=b1⋯bn 是同一字母表上的两个有限词。允许删除一个字符、插入一个字符、把一个字符替换成另一个,每次费用为 1;相等字符直接配对,费用为 0。Levenshtein 编辑距离是把 A 变成 B 所需的最小总费用。本页不把交换相邻字符当作一次操作。

令 D[i,j] 表示把前缀 A[1..i] 改成 B[1..j] 的距离。空前缀给出边界 D[i,0]=i、D[0,j]=j;内部按

D[i,j]=min{D[i−1,j]+1,删除 ai,D[i,j−1]+1,插入 bj,D[i−1,j−1]+[ai≠bj],配对或替换

计算。方括号中的命题为真时取 1,否则取 0。先填第零行与第零列,再按行递增填表,返回 D[m,n]。

这份动态规划同时支持重建:从 (m,n) 出发,选择一条使上式取等号的前驱边,记录对应操作,直到 (0,0),最后把记录倒序。本页固定“先对角,再删除,最后插入”的平局规则,使输出可复核;最小费用本身不依赖这条规则。

直觉

格点 (i,j) 的含义是:源串前 i 个字符已经消耗,目标串前 j 个字符已经生成。向下走只消耗源字符,是删除;向右走只生成目标字符,是插入;沿对角走同时消耗和生成,是配对或替换。所有边都增加 i+j,所以网格是一个有向无环图,可以按拓扑次序求最短路。

为什么只看最后一步就够了?一条对齐路径到达 (i,j),最后必然来自这三个前驱之一。若到那个前驱的部分并非最优,换成更便宜的部分不会影响最后一步,整条路径就还能变好。因此每个格子都由已经正确的较小前缀推出。归纳开始于空串边界,结束于右下角。

两条费用为 2 的对齐路径

“脚本”最好用消耗与生成的方式保存,而不是把每条命令都写成对当前字符串的绝对下标。前面的删除会移动后面的下标;对齐操作流则只维护两个游标。配对或替换读一个源字符并写一个目标字符,删除只读,插入只写,含义不会随执行进度漂移。

例子与边界

同样最优的两种解释 ​

取 A=CACTUS、B=CATCUS。完整表如下,行、列标题中的空字符表示长度为零的前缀。

A∖B 空 C A T C U S
空 0 1 2 3 4 5 6
C 1 0 1 2 3 4 5
A 2 1 0 1 2 3 4
C 3 2 1 1 1 2 3
T 4 3 2 1 2 2 3
U 5 4 3 2 2 2 3
S 6 5 4 3 3 3 2

在 D[3,3],前缀 CAC 对 CAT 的最后字符不同。对角候选为 D[2,2]+1=1,删除、插入候选都为 2,所以此格取 1。到 D[4,4] 时,三种候选都为 2,出现真正的解释歧义。

按本页的回溯顺序,保留前两个字符,把第三位 C 换成 T、第四位 T 换成 C,再保留 US,得到费用 2 的脚本。另一个同费脚本是删除第三位 C,得到 CATUS,再在 T 后插入 C。两条路径告诉我们:编辑距离认证最少操作数,不会自动判断现实中作者究竟做了哪一种修改。

为什么不是 1?两串长度相同,一次插入或删除无法保持长度;一次替换只能改变一个位置,而第三、第四位都不同。这给出下界 2,与脚本上界相遇。

先固定操作,再谈“距离” ​

若加入“相邻转置费用 1”,此例就会变成 1,需要另一套递推或状态;不能仍用三向公式然后把结果称作含转置的距离。若只允许等长逐位替换,得到的是Hamming 距离;它无法解释一次插入引起的后续错位。精确字符串匹配又是在文本里找原样出现的位置,目标不是把两个整串改成相同。

带权对齐可以把三种边的 1 换成相应费用,空串边界改成累计插删费用。此时计算的是规定网格模型下的最小对齐费用。若还希望它等于允许任意连续改写的费用,要排除“先把一个字符换成第三种字符,再换成目标字符更便宜”等捷径,或先对字符操作费用做最短路闭包;对称性、正定性也不能由非负费用自动得到。

推论与应用

填表需要 O((m+1)(n+1)) 时间、O((m+1)(n+1)) 存储;常写的 O(mn) 默认两串都非空。回溯至多走 m+n 步。只求距离时,每格只依赖当前行左邻与上一行,交换两串让短串作为列维后,可把工作数组压到 O(min(m,n)+1)。但覆盖旧行也会丢掉回溯所需的前驱,不能直接宣称同样空间里还保有整份脚本。

Hirschberg 分解用前向、反向滚动行找出最优路径的中间穿越点,再递归恢复脚本。若应用规定只有插入和删除,并且差异很少,Myers 算法则按修改数扩展前沿,避免填满网格。前者主要节省重建存储,后者利用小差异参数;两者解决的问题条件不同。

比较两个实现时,先检查脚本实际生成了目标串,再检查费用最优。相同距离而脚本不同常常只是平局规则不同,不能仅据此判错。包含空串、重复字符、全相等、全不同以及插删优于替换的例子,才能覆盖边界和回溯分支。

参考资料
  • Antti Laaksonen, Competitive Programmer’s Handbook, Chapter 7, “Edit distance”, printed pp. 74–75:作者公开教材。前缀递推、表格与路径重建。
  • Dave Mount, CMSC 451 Lecture 9, “Longest Common Subsequence and Edit Distance”, Spring 2025, pp. 5–8:官方讲义。三类操作、空串边界及编辑递推。
  • Robert A. Wagner and Michael J. Fischer, “The String-to-String Correction Problem”, Journal of the ACM 21(1), 1974, pp. 168–173:论文记录。插入、删除与替换的经典问题和算法。
关系图谱9 个相邻概念 · 2 类关系

拖动节点调整位置。

显示关系

显示:依赖

  1. 前置三跳
  2. 前置二跳
  3. 前置一跳
  4. 当前条目
  5. 后续一跳
  6. 后续二跳
  7. 后续三跳
文字版关系按与当前条目的最短距离分组
类型化关系