字符串相似度三種算法介紹


 

余弦相似度

計算公式為:

  P(A,B) = sqrt(A × B) / (|A| × |B|)

設有兩個字符串:

  1. ABCDEFG
  2. ABCHIJK

其中共有11個字符,為:

  A B C D E F G H I J K

如果,不考慮他們之間的關聯性以及順序等隱私,那么可以講這兩個字符串轉換成兩個11維空間中的向量:

  1. {1、1、1、1、1、1、1、0、0、0、0}
  2. {1、1、1、0、0、0、0、1、1、1、1}

那,計算他們之間的相似度為:

  P = sqrt(3) / (sqrt(7) × sqrt(7)) = 0.2474358297

矩陣相似度

給定兩個長度相等的字符串,在移動的過程中比較:

a b c d d a c b c b    
    a a d a c c b d d c

首先有幾個變量:

  1. n:字符串的長度,此時為10;
  2. m:相同的字符,此時為3,包括d、a、c;
  3. r:兩個字符串重疊部分,此時為8;

那么給出定義:

  1. 重疊率:L = r / n。
  2. 匹配率:M = m / n。
  3. 相似度:Q = M^2 × L = (m^2 / n^2) × (r / n)。

其實為什么這樣定義也很好理解,將Q變形一下就可以得到:

  Q = (m^2 / r^2) × (r / n)

前半部分表示了當前相同的比率,后半部分表示了重疊的比率,然后呢,廢話就不多說了。其實,還有一個要考慮的地方,舉個例子:

  str1:abcabc

  str2:abcdabc

str1str2的相似度是很高的,但是,在移位錯開的過程中根本沒辦法找到這種匹配。想想其實原因也是非常簡單的:把所有的字符都死板地粘合在了一起!那么,我們要做的其實就是將他們打散來匹配。首先,根據字符串A和字符串B來構造矩陣R

  Ai和Bi+j相同時,Rij = 1;否則,Rij = 0。

那么,現在要做的事情就是,在矩陣R中尋找一條路徑,使得這條路徑上的1最多,這個問題和求兩個字符串的最大匹配很像的DP問題,這里就不啰嗦了。

字符串編輯距離

還有一種衡量兩個字符串之間的差異性的方法是,計算兩個字符串轉換時候需要的最少操作,需要的操作越少說明這兩個字符串越相似。

假設字符串的操作只有三種:

  1. 插入一個字符;
  2. 刪除一個字符;
  3. 替換一個字符;

兩個字符串之間的編輯距離定義為:從字符串str1str2的最少的操作次數。首先,編輯距離是不會大於str1.length + str2.length的。假設求字符AB的編輯距離,考慮下面幾種情況:

如果A[i] = B[j],那么這時候還需要操作嗎?

這個時候的刪除和替換操作只會讓情況變得更壞,而且插入操作不會使情況變得更好,所以此時F(i, j) = F(i-1, j-1)

如果A[i] != B[j],怎么辦呢?

a、從F(i-1, j-1)變過來,這時候只需要把A[i]替換為B[j]即可;

b、從F(i-1, j)變過來,這時候只需要將A[i]刪除即可;

c、從F(i, j-1)變過來,這時候只需要在A[i]后插入字符B[j]即可;

那么此時,F(i, j) = min{F(i-1,j-1),F(i-1,j),F(i,j-1)} + 1

注:其中F(i, j)表示A[0..i]和B[0..j]之間的編輯距離。

 

附錄:Levenshtein Distance (編輯距離) 算法詳解

編輯距離即從一個字符串變換到另一個字符串所需要的最少變化操作步驟(以字符為單位,如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代表結果)。這在兩個字符串非常相似的情況下可以使對比速度速度大為增加。


免責聲明!

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



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