字符串相似度算法——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