字串編輯距離: 是一種字串之間相似性計算的方法。給定兩個字串S、T,將S轉換成T所需要的刪除,插入,替換操作的數量就叫做S到T的編輯路徑。而最短的編輯路徑就叫做字串S和T的編輯距離。
舉個例子:S=“eeba” T="abac" 我們可以按照這樣的步驟轉變:(1) 將S中的第一個e變成a;(2) 刪除S中的第二個e;(3)在S中最後添加一個c; 那麼S到T的編輯路徑就等於3。當然,這種變換並不是唯一的,但如果3是所有變換中最小值的話。那麼我們就可以說S和T的編輯距離等於3了。
動態規劃解決編輯距離
動態規劃(dynamic programming)是一種解決複雜問題最優解的策略。它的基本思路就是:將一個複雜的最優解問題分解成一系列較為簡單的最優解問題,再將較為簡單的的最優解問題進一步分解,直到可以一眼看出最優解為止。
動態規划算法是解決複雜問題最優解的重要演算法。其演算法的難度並不在於演算法本身的遞迴難以實現,而主要是編程者對問題本身的認識是否符合動態規劃的思想。現在我們就來看看動態規劃是如何解決編輯距離的。
還是這個例子:S=“eeba” T="abac" 。我們發現當S只有一個字元e、T只有一個字元a的時候,我們馬上就能得到S和T的編輯距離edit(0,0)=1(將e替換成a)。那麼如果S中有1個字元e、T中有兩個字元ab的時候,我們是不是可以這樣分解:edit(0,1)=edit(0,0)+1(將e替換成a後,在添加一個b)。如果S中有兩個字元ee,T中有兩個字元ab的時候,我們是不是可以分解成:edit(1,1)=min(edit(0,1)+1,
edit(1,0)+1, edit(0,0)+f(1,1)). 這樣我們可以得到這樣一些動態規劃公式:
如果i=0且j=0 edit(0, 0)=1
如果i=0且j>0 edit(0, j )=edit(0, j-1)+1
如果i>0且j=0 edit( i, 0 )=edit(i-1, 0)+1
如果i>0且j>0 edit(i, j)=min(edit(i-1, j)+1, edit(i,j-1)+1, edit(i-1,j-1)+f(i , j) )
小註:edit(i,j)表示S中[0.... i]的子串 si 到T中[0....j]的子串t1的編輯距離。f(i,j)表示S中第i個字元s(i)轉換到T中第j個字元s(j)所需要的操作次數,如果s(i)==s(j),則不需要任何操作f(i, j)=0; 否則,需要替換操作,f(i, j)=1 。
這就是將長字串間的編輯距離問題一步一步轉換成短字串間的編輯距離問題,直至只有1個字元的串間編輯距離為1。
演算法實現:
Python代碼
- #!/user/bin/env python
- # -*- coding: utf-8 -*-
-
- class arithmetic():
-
- def __init__(self):
- pass
- def levenshtein(self,first,second):
- if len(first) > len(second):
- first,second = second,first
- if len(first) == 0:
- return len(second)
- if len(second) == 0:
- return len(first)
- first_length = len(first) + 1
- second_length = len(second) + 1
- distance_matrix = [range(second_length) for x in range(first_length)]
- #print distance_matrix
- for i in range(1,first_length):
- for j in range(1,second_length):
- deletion = distance_matrix[i-1][j] + 1
- insertion = distance_matrix[i][j-1] + 1
- substitution = distance_matrix[i-1][j-1]
- if first[i-1] != second[j-1]:
- substitution += 1
- distance_matrix[i][j] = min(insertion,deletion,substitution)
- print distance_matrix
- return distance_matrix[first_length-1][second_length-1]
-
- if __name__ == "__main__":
- arith = arithmetic()
- print arith.levenshtein(
'GUMBOsdafsadfdsafsafsadfasfadsfasdfasdfs','GAMBOL00000000000dfasfasfdafsafasfasdfdsa' )