详细解释算法的原理以及计算过程。

这是由俄罗斯科学家 Vladimir Levenshtein 在 1965 年提出的这个概念。计算两个文本之间,从第一个文本转换成第二个文本所使用最少的步骤。

对一个文本进行以下三种操作

  • 替换一个字符
  • 删除一个字符
  • 添加一个字符

定义

如果分别用 |a| 和 |b| 表示 a,b 两个字符串的长度,那么它们的列文斯坦距离为 leva,b(|a|,|b|),它符合:

l(ai≠bj) 是一个指示函数,当 ai = bj 时,其值为 0,其他时候它等于 1 。

leva,b(i,j) 表示 a 的前 i 个字符与 b 的前 j 个字符之间的列文斯坦距离。(i 和 j 都是从 1 开始的下标)

注意:min运算中的第一个公式代表( 从 a 中)删除字符(以到达 b);第二个公式代表插入字符;第三个代表替换(取决于当前字符是否相同)。

代码

function levenshteinDistance (a, b) {
  const m = a.length
  const n = b.length
  const d = []
  for (let i = 0; i <= n; i++) d[i] = [i]
  for (let j = 0; j <= m; j++) d[0][j] = j
  for (let i = 1; i <= n; i++) {
    for (let j = 1; j <= m; j++) {
      d[i][j] = a[j - 1] === b[i - 1] ? d[i - 1][j - 1] : Math.min(d[i - 1][j], d[i][j - 1], d[i - 1][j - 1]) + 1
    }
  }
  return d[n][m]
}

$ lev_ {a,} $ b(|a|,|b|)

原理

例如计算 horse 单词转换成 ros 单词需要使用的最少步骤。

结果是最少需要三个步骤,算法如下:

步骤一:horse > rorse ("h" 替换 "r")
步骤二:rorse > rose (删除 "r")
步骤三:rose > ros (删除 "e")

实例

例如,计算 dance 转换成 pacaed 需要使用最少的步骤。(这个词是我作为例子生造的,没有其它特殊意义。)

那么先将这两个单词的每一个字符列出一个矩阵,每个单词的的开头保留一个空值。

先来计算第一行。两个空值是一致的,不需要进行任何的操作,所以将一个空值转换成空值,需要使用最少的步骤是 0。

将一个空值和 d 转换成空值,已知第一个步骤不需要进行任何的操作,但是第二个字符是多余的,需要将 d 删除,所以将空值和 d 转换成空值最少需要用到的步骤是 1。

那么继续往右计算,将一个空值和 da 转换成空值。和空值对比,da 是多余的字符,所以需要两个删除字符的步骤才能转换成和空值一致。

那么后面以此类推都是一样的,这里就可以“偷个懒”,往右直接加 1 就可以了。因为后面比空值都多一个字符,所以都需要多一个删除字符的步骤。

纵列也是一样的算法,将空值转换成空值和 p,需要使用删除字符,将 p 删掉,所以需要用到的步骤是 1。

下面的算法一样,直接往下加 1 即可。

继续计算第二行,将空值 d 转换成空值 p 所需的步骤,只需要将 d 替换成 p,所以需要用到的步骤是 1。计算的原理是这样的,如果遇到很长的字符这样算起来就太慢,下面介绍一个便捷的算法,先对比当前对应的字符是否一致。

如果不一致,就从矩阵中取这个范围内最小的值,也就是 0,然后再加 1。

那么就得出了将将空值 d 转换成空值 p 所需的步骤。

后面也是一样的算法,判断 a 和 p 是否一致,如果不一样,从矩阵中取这个范围内最小值,也就是 1,再加上 1。

那么就得出了将将空值 da 转换成空值 p 所需的步骤是 2。

还有一个更便捷的计算方法,判断在所有的范围里,是否包含 p,若不包含,根据上面的步骤,先计算出第一个所需的步骤,然后往后加 1 即可。

下面也是一样的算法, 先判断对应的字符,a 和 d 是否一致,若是不一致,从矩阵中取这个范围内最小值再加上 1。

所以得出的结果是 2。

如果对应字符一致,只需要从矩阵中取这个范围最小值,不需要加 1,因为相同的两个字符不需要任何的操作。

所以结果是 1。

后面的内容和 a 都不一致,从矩阵中取这个范围的最小值,也就是 1。因为 n 和 a 是不一致的,需要将最小值再加 1。剩下的往右加 1 即可。

继续往下计算,对应的 c 和 d 不一致,从矩阵中取这个范围内最小值再加上 1。

结果是 3。

往右也是一样的算法,判断 a 和 c 是否一致,如果不一致,从矩阵中取这个范围内最小值再加上 1。

不过需要注意的是最小值的位置不固定,可能会在第二个格子,也有可能会在第一个格子,所以不管存在哪个位置都取最小值。因为我看到有些解释是从第一个格子取值,但是这样的算法并不准确,在某些情况下会算错。

后面的算法都是一样的,就不一一举例了,只需要注意当前要计算对应的字符是否一致,判断是否需要加 1。

全部算完后,右下角的格子就是最终的结果,也就是说从 dance 转换成 pacaed 最少需要四个步骤。