編輯距離
編輯距離又稱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 ]