Levenshtein Distance
Levenshtein distance是一种常用的衡量两个字符串之间相似情况的度量。它是编辑距离的最为广泛使用的一种定义,大多数情况下编辑距离被用来特指Levenshtein距离。所谓字符串A和B的编辑距离就是指一个字符串A需要经过多少次编辑可以得到字符串B。
要计算Levenshtein距离可以用动态规划的方法来进行。
计算
假设我们需要计算两个字符串s和t的Levenshtein距离,其中s长为m,t长为n。记d[i,j]表示将子串s[1...i]转换为t[1...j]所需的最少操作数,那么Levenshtein距离即为d[m,n]。
有如下几个事实成立:
- d[i,0]=i,d[0,j]=j。因为s[1..i]可以通过i次删除操作变为空字符串t[1...0],所以d[i,0]=i。同样的d[0,j]=0。
- 如果s[i]=t[j]且d[i-1,j-1]=k,那么d[i,j]=k。因为最后一个字符相同,所以在将字符串s[1...i]变为t[1...j]时,只需考虑子串s[1...(i-1)]变为t[1...(j-1)],由条件可知需k次编辑。
- 在其它情况下,距离为下面三种可能操作中编辑数最小的那个:
- 插入。若d[i,j-1]=k,那么通过插入t[j]在k+1次编辑时可以得到t[1...j]。
- 删除。若d[i-1,j]=k,那么通过删除s[i]在k+1次编辑时可以得到t[1...j]。
- 替换。若d[i-1,j-1]=k,那么可以通过将s[i]替换为t[j]在k+1次编辑时得到t[1...j]。
代码
有兴趣的童鞋见这里。
可改进之处
- 可以减少空间复杂度到O(min(m,n)),而不是O(m*n),因为在计算过程中每次仅需要前一行和当前行的数据。
- 可以将距离标准化到区间[0, 1]。
- 如果只关心距离是否小于某个给定的阈值k,那么只需要计算对角线上宽度为2k+1的方块。此时算法可以在O(kI)的时间内结束,其中I为短字符串的长度。
- 对于插入、删除、替换操作可以分别赋予不同的惩罚函数。同样可以根据插入、删除、替换的字符定义不同的惩罚函数。
- 将矩阵的首行置为0,则该算法可以用来在文本中进行模糊查找字符串。
- 由于计算过程前后的相关性,这个算法很难并行。However, all the cost values can be computed in parallel, and the algorithm can be adapted to perform the minimum function in phases to eliminate depedencies. (TODO: 暂时不是很理解它的做法,先记下来)
- 通过检查对角线而非行,并使用延迟计算(lazy evaluation)技术,可以在O(m*(1+d))的时间内(d为Levenshtein距离)完成距离的计算。当距离很小时,这种算法比常规的动态规划要快很多。
上下限的性质
- 最小为两字符串长度之差。
- 最大为长字符串长度。
- 当且仅当两字符串相同时为0。
- 如果两字符串长度相同,其上限为Hamming距离。
延伸阅读:与其他编辑距离
Levenshtein距离并不是唯一常用的编辑距离,通过定义不同编辑操作可以得到不同的距离度量:
- 最长公共子序列(longest common subsequence):只允许插入和删除,不能替换。
- Demerau-Levenshtein距离:允许插入、删除、替换以及相邻字符间的交换。
- Hamming距离:只允许替换,因此也仅用于长度相同的字符串。
blog comments powered by Disqus