字符串相似度算法——Levenshtein Distance算法


Levenshtein Distance 算法,又叫 Edit Distance 算法,是指兩個字符串之間,由一個轉成另一個所需的最少編輯操作次數。許可的編輯操作包括將一個字符替換成另一個字符,插入一個字符,刪除一個字符。一般來說,編輯距離越小,兩個串的相似度越大。

 

算法實現原理圖解:

a.首先是有兩個字符串,這里寫一個簡單的 abc 和 abe

b.將字符串想象成下面的結構。

A 處 是一個標記,為了方便講解,不是這個表的內容。

  abc a b c
abe 0 1 2 3
a 1 A處    
b 2      
e 3      

c.來計算 A 處 出得值

它的值取決於:左邊的 1、上邊的 1、左上角的 0。

按照 Levenshtein distance 的意思:

上面的值加 1 ,得到 1+1=2 ,

左面的值加 1 ,得到 1+1=2 ,

左上角的值根據字符是否相同,相同加 0 ,不同加 1 。A 處由於是兩個 a 相同,左上角的值加 0 ,得到 0+0=0 。

然后從我們上面計算出來的 2,2,0 三個值中選取最小值,所以 A 處的值為 0 。

d.於是表成為下面的樣子

  abc a b c
abe 0 1 2 3
a 1 0    
b 2 B處    
e 3      

在 B 處 會同樣得到三個值,左邊計算后為 3 ,上邊計算后為 1 ,在 B 處 由於對應的字符為 a、b ,不相等,所以左上角應該在當前值的基礎上加 1 ,這樣得到 1+1=2 ,在(3,1,2)中選出最小的為 B 處的值。

e.於是表就更新了

 

  abc a b c
abe 0 1 2 3
a 1 0    
b 2 1    
e 3 C處    

C 處 計算后:上面的值為 2 ,左邊的值為 4 ,左上角的:a 和 e 不相同,所以加 1 ,即 2+1 ,左上角的為 3 。

在(2,4,3)中取最小的為 C 處的值。

f.於是依次推得到

    a b c
  0 1 2 3
a 1 A處 0 D處 1 G處 2
b 2 B處 1 E處 0 H處 1
e 3 C處 2 F處 1 I處 1

 

I 處: 表示 abc 和 abe 有1個需要編輯的操作( c 替換成 e )。這個是需要計算出來的。

同時,也獲得一些額外的信息:

A處: 表示a      和a       需要有0個操作。字符串一樣

B處: 表示ab    和a       需要有1個操作。

C處: 表示abe  和a       需要有2個操作。

D處: 表示a      和ab     需要有1個操作。

E處: 表示ab    和ab     需要有0個操作。字符串一樣

F處: 表示abe  和ab     需要有1個操作。

G處: 表示a      和abc   需要有2個操作。

H處: 表示ab    和abc   需要有1個操作。

I處: 表示abe   和abc    需要有1個操作。

g.計算相似度

先取兩個字符串長度的最大值 maxLen,用 1-(需要操作數 除 maxLen),得到相似度。

例如 abc 和  abe  一個操作,長度為 3 ,所以相似度為 1-1/3=0.666 。

 

以上就是整個算法的推導過程,但是至於為什么能算出相似度,還是不太懂。而且我發現這個算法有個很坑的地方,就是有時候根據算法推算出的結果,會和我們想象中的不一樣:

例如:字符串 abcd 和字符串 def ,根據算法算出來的相似度為 0,可是明明是由一個相同字符 d 的,至少應該是有一定相似度,即使很相似度很低,但是也不應該為0才對。但是字符串 abcd 和字符串 aert ,同樣是只有一個相同字符 a ,但是根據算法算出來的相似度卻為 0.25 。

根據最開始對算法的介紹,使用替換、刪除、插入這三種操作方式,字符串 abcd 和字符串 aert 最少需要 3 步就能完成轉變,而字符串 abcd 和字符串 def 卻最少需要4步才能完成轉變,兩個字符串的最大長度才為 4 ,而完成轉換卻至少需要 4 步,相似度為 0 好像也能說的通,但是從表面來看,明明有一個相同的字符d,相似度為0,讓我覺得很怪異,所以我認為再使用的時候需要慎重。

 

下面是我的 C# 代碼實現:

復制代碼
 1 using UnityEngine;  2 using System.Collections;  3 using System;  4  5 public class EditorDistance  6 {  7 /// <summary>  8 /// 比較兩個字符串的相似度,並返回相似率。  9 /// </summary> 10 /// <param name="str1"></param> 11 /// <param name="str2"></param> 12 /// <returns></returns> 13 public static float Levenshtein(string str1, string str2) 14  { 15 char[] char1 = str1.ToCharArray(); 16 char[] char2 = str2.ToCharArray(); 17 //計算兩個字符串的長度。 18 int len1 = char1.Length; 19 int len2 = char2.Length; 20 //建二維數組,比字符長度大一個空間 21 int[,] dif = new int[len1 + 1, len2 + 1]; 22 //賦初值 23 for (int a = 0; a <= len1; a++) 24  { 25 dif[a, 0] = a; 26  } 27 for (int a = 0; a <= len2; a++) 28  { 29 dif[0, a] = a; 30  } 31 //計算兩個字符是否一樣,計算左上的值 32 int temp; 33 for (int i = 1; i <= len1; i++) 34  { 35 for (int j = 1; j <= len2; j++) 36  { 37 if (char1[i - 1] == char2[j - 1]) 38  { 39 temp = 0; 40  } 41 else 42  { 43 temp = 1; 44  } 45 //取三個值中最小的 46 dif[i, j] = Min(dif[i - 1, j - 1] + temp, dif[i, j - 1] + 1, dif[i - 1, j] + 1); 47  } 48  } 49 //計算相似度 50 float similarity = 1 - (float)dif[len1, len2] / Math.Max(len1, len2); 51 return similarity; 52  } 53 54 /// <summary> 55 /// 求最小值 56 /// </summary> 57 /// <param name="nums"></param> 58 /// <returns></returns> 59 private static int Min(params int[] nums) 60  { 61 int min = int.MaxValue; 62 foreach (int item in nums) 63 { 64 if (min > item) 65 { 66 min = item; 67 } 68 } 69 return min; 70 } 71 }
復制代碼

 


免責聲明!

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



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