编辑距离
编辑距离又称Levenshtein距离,是指将一个字符串转为另一个字符串所需的字符编辑次数,包括以下三种操作:
插入 - 在任意位置插入一个字符
删除 - 将任意一个字符删除
替换 - 将任意一个字符替换为另一个字符
最小编辑距离是指将一个字符串转为另一个字符串所需的最少字符编辑次数。
思路
计算最小编辑距离需要用到动态规划 。对于两个字符串A A A 和B B B ,设f ( i , j ) f(i, j) f ( i , j ) 表示将A A A 的前i i i 个字符构成的子串转为B B B 的前j j j 个字符构成的子串所需的最少次数,那么f ( i , 0 ) = i f(i, 0) = i f ( i , 0 ) = i ,f ( 0 , j ) = j f(0, j) = j f ( 0 , j ) = j ,从A A A 到B B B 的最小编辑距离即f ( m , n ) f(m, n) f ( m , n ) ,m m m 和n n n 分别为A A A 和B B B 的长度。
由于f ( i , 0 ) f(i, 0) f ( i , 0 ) 和f ( 0 , j ) f(0, j) f ( 0 , j ) 已确定,可以考虑从f ( i , j ) f(i, j) f ( i , j ) 递推到f ( i + 1 , j ) f(i+1, j) f ( i + 1 , j ) 、f ( i , j + 1 ) f(i, j+1) f ( i , j + 1 ) 和f ( i + 1 , j + 1 ) f(i+1, j+1) f ( i + 1 , j + 1 ) ,或者反过来,考虑从f ( i − 1 , j ) f(i-1, j) f ( i − 1 , j ) 、f ( i , j − 1 ) f(i, j-1) f ( i , j − 1 ) 、f ( i − 1 , j − 1 ) f(i-1, j-1) f ( i − 1 , j − 1 ) 递推到f ( i , j ) f(i, j) f ( i , j ) 。显然,前两种情况只需要在最后插入一个字符,即f ( i , j ) = f ( i − 1 , j ) + 1 f(i, j) = f(i-1, j)+1 f ( i , j ) = f ( i − 1 , j ) + 1 ,f ( i , j ) = f ( i , j − 1 ) + 1 f(i, j) = f(i, j-1)+1 f ( i , j ) = f ( i , j − 1 ) + 1 。而最后一种情况需要比较A A A 的第i i i 个字符和B B B 的第j j j 个字符,若相等,则f ( i , j ) = f ( i − 1 , j − 1 ) f(i, j) = f(i-1, j-1) f ( i , j ) = f ( i − 1 , j − 1 ) ;否则需要进行替换操作,即f ( i , j ) = f ( i − 1 , j − 1 ) + 1 f(i, j) = f(i-1, j-1)+1 f ( i , j ) = f ( i − 1 , j − 1 ) + 1 。因此f ( i , j ) f(i, j) f ( i , j ) 取三者最小值,递推公式为
f ( i , j ) = m i n { ( f ( i − 1 , j ) + 1 f ( i , j − 1 ) + 1 f ( i − 1 , j − 1 ) + { 0 , A [ i ] = B [ j ] 1 , o t h e r w i s e
f(i,j) = min
\begin{cases}
(f(i-1, j)+1 \\
f(i, j-1)+1 \\
f(i-1, j-1)+
\begin{cases}
0, A[i] = B[j] \\
1, otherwise
\end{cases}
\end{cases}
f ( i , j ) = m i n ⎩ ⎪ ⎪ ⎪ ⎨ ⎪ ⎪ ⎪ ⎧ ( f ( i − 1 , j ) + 1 f ( i , j − 1 ) + 1 f ( i − 1 , j − 1 ) + { 0 , A [ i ] = B [ j ] 1 , o t h e r w i s e
复杂度
时间复杂度和空间复杂度均为O ( m n ) O(mn) O ( m n ) ,其中m m m 和n n n 分别为A A A 和B B B 的长度。
Python代码
完整代码见Github 。
def Levenshtein ( str1, str2) :
m = len ( str1) + 1
n = len ( str2) + 1
dp = [ [ 0 for _ in range ( n) ] for _ in range ( m) ]
for i in range ( m) :
dp[ i] [ 0 ] = i
for j in range ( n) :
dp[ 0 ] [ j] = j
for i in range ( 1 , m) :
for j in range ( 1 , n) :
if ( str1[ i- 1 ] == str2[ j- 1 ] ) :
dp[ i] [ j] = dp[ i- 1 ] [ j- 1 ]
else :
dp[ i] [ j] = 1 + min ( dp[ i- 1 ] [ j- 1 ] , dp[ i- 1 ] [ j] , dp[ i] [ j- 1 ] )
return dp[ m- 1 ] [ n- 1 ]