1. 程式人生 > >編輯距離 演算法詳述計算兩個字串差異 c++程式碼

編輯距離 演算法詳述計算兩個字串差異 c++程式碼

編輯距離即從一個字串變換到另一個字串所需要的最少變化操作步驟(以字元為單位,如son到sun,s不用變,將o->s,n不用變,故操作步驟為1)。

為了得到編輯距離,我們畫一張二維表來理解,以beauty和batyu為例:

圖示如1單元格位置即是兩個單詞的第一個字元[b]比較得到的值,其值由它上方的值(1)、它左方的值(1)和、它左上角的值(0)來決定。當單元格所在的行和列所對應的字元(如3對應的是a和b)相等時,它左上角的值+0,否則加1(如在1處,[b]=[b]故左上角的值加0即0+0=0,而在2處[b]!=[b] 故左上角的值加1即1+1=2)。然後再將單元格的左單元格和上單元格的值分別加1,(,然後取相加後得到的三個結果的最小值作為該單元的值如1

處相加後其左上、左、上的值為(0,2,2),故1單元格的值為0,而在3處,得到的值為(2,3,1),故3單元格的值為1)。

演算法證明

這個演算法計算的是將s[1…i]轉換為t[1…j](例如將beauty轉換為batyu)所需最少的運算元(也就是所謂的編輯距離),這個運算元被儲存在d[i,j](d代表的就是上圖所示的二維陣列)中。

  • 在第一行與第一列肯定是正確的,這也很好理解,例如我們將beauty轉換為空字串,我們需要進行的運算元為beauty的長度(所進行的操作為將beauty所有的字元丟棄)。
  • 我們對字元可能進行的操作有三種:
  • 將s[1…n]轉換為t[1…m]當然需要將所有的s轉換為所有的t,所以,d[n,m](表格的右下角)就是我們所需的結果。
  • 如果我們可以使用k個運算元把s[1…i]轉換為t[1…j-1],我們只需要把t[j]加在最後面就能將s[1…i]轉換為t[1…j],運算元為k+1
  • 如果我們可以使用k個運算元把s[1…i-1]轉換為t[1…j],我們只需要把s[i]從最後刪除就可以完成轉換,運算元為k+1
  • 如果我們可以使用k個運算元把s[1…i-1]轉換為t[1…j-1],我們只需要在需要的情況下(s[i] != t[j])把s[i]替換為t[j],所需的運算元為k+cost(cost代表是否需要轉換,如果s[i]==t[j],則cost為0,否則為1)。

這個證明過程只能證明我們可以得到結果,但並沒有證明結果是最小的(即我們得到的是最少的轉換步驟)。所以我們引進了另外一個演算法,即d[i,j]儲存的是上述三種操作中運算元最小的一種。這就保證了我們獲得的結果是最小的運算元

可能進行的改進

    • 現在的演算法複雜度為O(mn),可以將其改進為O(m)。因為這個演算法只需要上一行和當前行被儲存下來就可以了。
    • 如果需要重現轉換步驟,我們可以把每一步的位置和所進行的操作儲存下來,進行重現。
    • 如果我們只需要比較轉換步驟是否小於一個特定常數k,那麼只計算高寬寬為2k+1的矩形就可以了,這樣的話,演算法複雜度可簡化為O(kl),l代表參加對比的最短string的長度。
    • 我們可以對三種操作(新增,刪除,替換)給予不同的權值(當前演算法均假設為1,我們可以設新增為1,刪除為0,替換為2之類的),來細化我們的對比。
    • 如果我們將第一行的所有cell初始化為0,則此演算法可以用作模糊字元查詢。我們可以得到最匹配此字串的字串的最後一個字元的位置(index number),如果我們需要此字串的起始位置,我們則需要儲存各個操作的步驟,然後通過演算法計算出字串的起始位置。
    • 這個演算法不支援平行計算,在處理超大字串的時候會無法利用到平行計算的好處。但我們也可以並行的計算cost values(兩個相同位置的字元是否相等),然後通過此演算法來進行整體計算。
    • 如果只檢查對角線而不是檢查整行,並且使用延遲驗證(lazy evaluation),此演算法的時間複雜度可優化為O(m(1+d))(d代表結果)。這在兩個字串非常相似的情況下可以使對比速度速度大為增加。、
  1. #include <stdio.h>   
  2. #include <string.h>   
  3. char s1[1000],s2[1000];     
  4. int min(int a,int b,int c) {     
  5.     int t = a < b ? a : b;     
  6.     return t < c ? t : c;     
  7. }     
  8. void editDistance(int len1,int len2)  
  9. {     
  10.     int** d=newint*[len1+1];  
  11.     for(int k=0;k<=len1;k++)  
  12.         d[k]=newint[len2+1];    
  13.     int i,j;     
  14.     for(i = 0;i <= len1;i++)     
  15.         d[i][0] = i;     
  16.     for(j = 0;j <= len2;j++)     
  17.         d[0][j] = j;     
  18.     for(i = 1;i <= len1;i++)     
  19.         for(j = 1;j <= len2;j++)  
  20.         {     
  21.             int cost = s1[i] == s2[j] ? 0 : 1;     
  22.             int deletion = d[i-1][j] + 1;     
  23.             int insertion = d[i][j-1] + 1;     
  24.             int substitution = d[i-1][j-1] + cost;     
  25.             d[i][j] = min(deletion,insertion,substitution);     
  26.         }     
  27.         printf("%d\n",d[len1][len2]);   
  28.         for(int k=0;i<=len1;k++)  
  29.             delete[] d[k];  
  30.         delete[] d;  
  31. }     
  32. int main()  
  33. {     
  34.     while(scanf("%s %s",s1,s2) != EOF)     
  35.         editDistance(strlen(s1),strlen(s2));     
  36. }