【算法】 字符串相似度問題


  之前有說過最長公共子序列的問題,類似的還有一個兩個字符串相似度的問題。

  所謂相似度就是指一個字符串要至少通過多少次變化(插入一個新字符,刪除一個字符,替換一個字符)才能變成另一個字符串。

  在python中,我們有Levenshtein模塊可以非常快速地得到結果:Levenshtein.distance("string1","string2"),而如果想要通過算法自己解決,它和LCS問題類似,也是一個動態規划可以完成的問題。

  算法描述:

  有字符串a,b。兩者長度len(a),len(b)分別是m,n。

  取a,b的首字母進行比較,如果相同則操作數C=0,否則C=1,再各取兩個字符串的第二個、第三個字符依次比較…當比較到a[i],b[i]這兩個字符時,有:

  1. 若兩字符相同,則此時操作數應該等於a[i-1],b[i-1]兩者的操作數,因為相比於這兩者,現在的情況只不過是在兩者的末尾又都添加了一個相同字符,對於計算操作數而言並不增加。

  2. 若兩字符不同,考慮此時的操作數C的來源可能性有三種:

    ①  a[i-1],b[i]比較的操作數 C0 + 1,好比是認為a[i]和b[i]是a[i-1]和b[i]比較的基礎上再為a[i-1]增加了一個字符,操作數+1

    ②  a[i],b[i-1]比較的操作數 C1 + 1,理解類似上面的

    ③  a[i-1],b[i-1]比較的操作數 C2 + 1,好比認為給a[i-1]和b[i-1]同時添加一個字符(這一步不用算操作數,因為兩者同時添加的)后,再進行一次字符的替換。

  綜合來說,字符串a1a2a3…an和b1b2b3…bn比較得到的操作數C = min(C0+1,C1+1,C2+k)其中k=0(an=bn時)或1(an!=bn時)

  具體實現:

  和LCS問題類似,通過構建一個二維數組來實現算法。數組c的行數和列數分別是兩個字符串的長度+1,這個加上的1個字符可以看做是一個空字符加在兩個字符串最前面,在構建數組的時候充當邊界條件判斷。(其實不要這個空字符貌似也可以,這樣的話就是需要額外判斷一下兩首字母是否相同才能初始化整個數組,有點麻煩)比如比較"cherry"和"berry"的相似度,有這樣一個數組:

  b e r r y
0 1 2 3 4 5
c 1 x        
h 2        
e 3    
r 4        
r 5      
y 6       y

  數組中的每一個元素都是相對應的行列數i,j時cherry[:i]和berry[:j]之間比較的操作數。自然表格第一行和第一列是一個從0開始到字符串長度的數列,因為字符串和空串比較,總是要增加字符串長度個字符,所以總的來看就是這個數字。這樣我們就可以初始化這個矩陣的第一行第一列,然后剩余部分交給上面的判斷規則來填充,最后得到的最右下角的數據y便是我們要求的總的操作數了。

  下面是python代碼:

sa = "franknihao"
sb = "takanashi"
sa = " "+sa
sb = " "+sb
c = [[None for j in range(len(sb))] for i in range(len(sa))]
for j in range(len(sb)):
    c[0][j] = j
for i in range(len(sa)):
    c[i][0] = i
def fill(sa,sb,c):
    for i in range(1,len(sa)):
        for j in range(1,len(sb)):
            if sa[i] == sb[j]:
                c[i][j] = c[i-1][j-1]
            else:
                c[i][j] = min(c[i-1][j]+1,c[i][j-1]+1,c[i-1][j-1]+1)
fill(sa,sb,c)
print c[len(sa)-1][len(sb)-1]

 

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM