/编辑距离
演示中
1

把两个词排成表格的行与列

组合数学

编辑距离

拼写检查怎么知道你想打的是哪个词?

拼写检查怎么知道你想打的是哪个词?

你手一抖打成了 recieve,输入法立刻在下面提示 receive;你搜 kitten 打成了 sitting,引擎也能猜个八九不离十。它凭什么认定这两个词"很接近",而不是别的词?

背后有一把专门量"词与词差多远"的尺子——编辑距离。它问的是一个很朴素的问题:把一个词改成另一个,最少要动几次手?

三种基本操作

允许的"动手"只有三种,每种算一步:

  • :插入一个字符(catcart
  • :删掉一个字符(cartcar
  • :替换一个字符(catcut

把一个词变成另一个,用掉的最少步数,就是它们的编辑距离(也叫 Levenshtein 距离)。

比如 kittensitting:把 k 改成 s、e 改成 i、末尾加一个 g——三步,一步都不能少。所以它们的编辑距离是 3

怎么保证"最少"?——填一张表

难点在于"最少"。挨个去试组合会爆炸。聪明的办法是动态规划:把大问题拆成小问题,结果填进一张表里。

把两个词分别摆在表格的行和列。格子 Di,jD_{i,j} 表示:第一个词的前 ii 个字母,变成第二个词的前 jj 个字母,最少要几步。

关键洞察是——每个格子,只取决于它的三个邻居

想算 Di,jD_{i,j},看它上面、左边、左上三个已经算好的格子,挑一条最省的路走过来。

  • 上面来 = 删一个字符,Di1,j+1D_{i-1,j}+1
  • 左边来 = 增一个字符,Di,j1+1D_{i,j-1}+1
  • 左上来 = 改一个字符;若这两个字符本来就相同,则不花钱(+0),否则 +1

于是递推式就是:

Di,j=min(Di1,j+1,  Di,j1+1,  Di1,j1+[aibj])D_{i,j} = \min\big(\,D_{i-1,j}+1,\; D_{i,j-1}+1,\; D_{i-1,j-1}+[a_i \neq b_j]\,\big)

第一行、第一列先填好(从空串出发,只能一路插入或删除:Di,0=iD_{i,0}=iD0,j=jD_{0,j}=j)。然后一格格往右下推,右下角那个数,就是答案

回溯:看清它到底怎么改的

填完表还能"倒着走回去":从右下角出发,每步回到那个给出最小值的邻居,一路退到左上角。这条路径就还原了最优的对齐方式——哪里匹配、哪里增、哪里删、哪里改,一目了然。动画里红线画的就是这条路。

它藏在哪些地方

  • 拼写检查 / 自动纠错 / 搜索"你是不是要找":找编辑距离最小的候选词。
  • 生物信息:DNA、蛋白质的序列比对,本质就是带权的编辑距离。
  • 版本对比git diff、文档 diff 找最小改动,思路同源。

下次输入法替你纠错时,别小看它——那是一张表格,悄悄帮你算了几百条改法里最省的一条。

同分类推荐
阿基米德的胃痛拼图

14 块拼成正方形,到底有多少种拼法?