字串編輯距離是一個很經典的問題了,以前參加編程比賽、筆試面試的時候經常會碰到,今天看《編程之美》的3.3節中又遇到了該問題,又重新回顧了一邊,順便在這裡總結一下。
【問題描述】
給定一個源字串和目標字串,能夠對源串進行如下操作:
1.在給定位置上插入一個字元
2.替換任一字元
3.刪除任一字元
求通過以上操作使得源字串和目標字串一致的最小操作步數。(問題的詳細描述參考《數學之美》3.3)。
【解題思想】
簡單描述一下解該題的思想,源字串和目標字串分別為str_a、str_b,二者的長度分別為la、lb,定義f[i,j]為子串str_a[0...i]和str_b[0...j]的最小編輯距離,簡單分析可知求得的str_a[0...i]和str_b[0...j]的最小編輯距離有一下三種可能:
(1)去掉str_a[0...i]的最後一個字元跟str_b[0...j]匹配,則f[i, j]的值等於f[i-1, j]+1;
(2)去掉str_b[0...j]的最後一個字元跟str_a[0...i]匹配,則f[i, j]的值等於f[i, j-1]+1;
(3)去掉str_a[0...i]和str_b[0...j]的最後一個字元,讓二者匹配求得f[i-1, j-1],計算f[i, j]時要考慮當前字元是否相等,如果str_a[i]==str_b[j]說明該字元不用編輯,所以f[i, j]的值等於f[i-1,
j-1],如果str_a[i]!=str_b[j]說明該字元需要編輯一次(任意修改str_a[i]或者str_b[j]即可),所以f[i, j]的值等於f[i-1, j-1]+1。
因為題目要求的是最小的編輯距離,所以去上面上中情況中的最小值即可,因此可以得到遞推公式:
f[i, j] = Min ( f[i-1, j]+1, f[i, j-1]+1, f[i-1, j-1]+(str_a[i]==str_b[j] ? 0 : 1) )
是維基百科【參考文獻1】中的遞推式,實際上與上面的是一樣的:
【解題方法】
有了上面的遞推公式便可以編程實現了,一般會有兩種解法。
第一種:遞迴方法(也是動態規劃方法),通過上面的遞推公式寫出遞迴程式,這裡就不貼出原始碼了,網上到處都是。用遞迴方法要注意一個問題,從f[la-1, lb-1]往下遞迴時會有重複計算f[x, y](x<la-1, y<lb-1)的情況,是非常影響程式效率的,所以一般會建立f值得緩衝,因此遞迴方法的程式會有兩個版本,一個是最簡單最原始的版本——不帶f值緩衝,另一個版本帶f值緩衝,維基百科【參考文獻1】中給出了這兩個版本的程式。
第二種:遞推方法(也可稱為矩陣標記法),通過分析可知可以將f[i, j]的計算在一個二維矩陣中進行,上面的遞推式實際上可以看做是矩陣單元的計算遞推式,只要把矩陣填滿了,f[la-1, lb-1]的值就是要求得最小編輯距離,具體的過程這裡不再贅述,可以參考【參考文獻2】,程式網上也有很多。
【文獻3】提供了上面提到的三種版本的程式。
【補充】
最後補充一下“字串相似性”的定義,《編程之美》3.3節中提到:相似性表示為1/(最小編輯距離 + 1),比如abcdefg和abcdef最小編輯距離為1(通過abcdefg刪除g一個操作),這樣相似性為1/2 = 0.5。
【參考文獻】
http://en.wikipedia.org/wiki/Levenshtein_distance
http://www.cnitblog.com/ictfly/archive/2005/12/27/5828.aspx
http://blog.163.com/kevinlee_2010/blog/static/16982082020111123111835146/