前言
目前計算句子相似性有很多不同的方案,比如基於語義詞典的方法、基於相同詞彙的方法、基於統計的方法和基於編輯距離的方法。這篇文章先介紹編輯距離的基礎。
編輯距離
編輯距離其實就是指把一個字串轉換為另外一個字串所需要的最小編輯操作的代價數。包括插入字元、替換字元和刪除字元。編輯距離越小,相似度越大。
比如我們要將what
轉換成where
,可能是將 a -> e,接著 t -> r ,變為wher
,最後新增 e,完成。因為每一步都可以插入、刪除或替換,那麼如何才能在最終得到一個最小的代價,這是就會用到動態規劃來求解。
假設我們有長度分別為i、j
的兩個字串,設編輯距離為edit(i,j)
。接著我們看下,如果它們最後的字元相等,則編輯距離其實等於edit(i-1,j-1)
。而如果最後的字元不相等,那麼我們可以通過插入或替換來使其相等,但是不同的操作對應的代價不相同,如果插入則為edit(i,j-1)+1
或 eidit(i-1,j)+1
,替換則為edit(i-1,j-1)+1
。
求解
用以下動態方程來表示:
其中舉個前面的例子,what 到 where,假設兩個字元分別為空時對應的代價數如下,
編輯距離 | 空 | w | h | a | t |
空 | 0 | 1 | 2 | 3 | 4 |
w | 1 | ||||
h | 2 | ||||
e | 3 | ||||
r | 4 | ||||
e | 5 |
動態規劃後為:
編輯距離 | 空 | w | h | a | t |
空 | 0 | 1 | 2 | 3 | 4 |
w | 1 | 0 | 1 | 2 | 3 |
h | 2 | 1 | 0 | 1 | 2 |
e | 3 | 2 | 1 | 1 | 2 |
r | 4 | 3 | 2 | 2 | 2 |
e | 5 | 4 | 3 | 3 | 3 |
在整個動態規劃過程中一直都是選擇最小的代價數,所以最終的3即是這兩個字串的最小代價數,即編輯距離。
github
https://github.com/sea-boat/TextAnalyzer/blob/master/src/main/java/com/seaboat/text/analyzer/distance/CharEditDistance.java
實現
public class EditDistance {
public static int getEditDistance(String s, String t) {
int d[][];
int n;
int m;
int i;
int j;
char s_i;
char t_j;
int cost;
n = s.length();
m = t.length();
if (n == 0) {
return m;
}
if (m == 0) {
return n;
}
d = new int[n + 1][m + 1];
for (i = 0; i <= n; i++) {
d[i][0] = i;
}
for (j = 0; j <= m; j++) {
d[0][j] = j;
}
for (i = 1; i <= n; i++) {
s_i = s.charAt(i - 1);
for (j = 1; j <= m; j++) {
t_j = t.charAt(j - 1);
cost = (s_i == t_j) ? 0 : 1;
d[i][j] = Math.min(d[i - 1][j] + 1, d[i][j - 1] + 1);
d[i][j] = Math.min(d[i][j], d[i - 1][j - 1] + cost);
}
}
return d[n][m];
}
public static void main(String[] args) {
EditDistance ed = new EditDistance();
System.out.println(ed.getEditDistance("what", "where"));
}
}
複製程式碼
-------------推薦閱讀------------
跟我交流,向我提問:
公眾號的選單已分為“讀書總結”、“分散式”、“機器學習”、“深度學習”、“NLP”、“Java深度”、“Java併發核心”、“JDK原始碼”、“Tomcat核心”等,可能有一款適合你的胃口。
歡迎關注: