編輯距離單詞匹配算法
1、介紹
編輯距離(Edit Distance),在本文指的是Levenshtein距離,也就是字符串S1通過插入、修改、刪除三種操作最少能變換成字符串S2的次數。例如:S1 = abc,S2 = abf,編輯距離d = 1(只需將c修改爲f)。在本文中將利用動態規劃的算法思想對字符串的編輯距離求解。
定義:S1、S2表示兩個字符串,S1(i)表示S1的第一個字符,d[i, j]表示S1的第i個前綴到S2的第j個前綴(例如:S1 = ”abc”,S2 = ”def”,求解S1到S2的編輯距離爲d[3, 3])。
若S1 = ”abc”, S2 = ”dec”,此時它們的編輯距離爲d[3, 3] = 2,觀察兩個字符串的最後一個字符是相同的,也就是說S1(3) = S2(3)不需要做任何變換,故S1 = ”abc”, S2 = ”dec” <= > S1’ = ”ab”, S2’ = ”de”,即當S1[i] = S[j]時,d[i, j] = d[i-1,j -1]。得到公式:d[i, j] = d[i - 1, j - 1] (S1[i] = S2[j])
上面一條得出了當S1[i] = S2[j]的計算公式,顯然還有另一種情況就是S1[i] ≠ S2[j],若S1 = ”abc”, S2 = ”def”。S1變換到S2的過程可以“修改”,但還可以通過“插入”、“刪除”使得S1變換爲S2。
1)在S1字符串末位插入字符“f”,此時S1 = ”abcf”,S2 = ”def”,此時即S1[i] = S2[j]的情況,S1變換爲S2的編輯距離爲d[4, 3] = d[3, 2]。所以得出d[i, j]=d[i, j - 1] + 1。(+1是因爲S1新增了”f”)
2)在S2字符串末位插入字符“c”,此時S1 = ”abc”,S2 = ”defc”,此時即S1[i] = S[j]的情況,S1變換爲S2的編輯距離爲d[3, 4] = d[2, 3]。所以得出d[i, j]=d[i - 1, j] + 1,實際上這是對S1做了刪除。(+1是因爲S2新增了”c”)
3)將S1字符串末位字符修改爲”f”,此時S1 = ”abf”,S2 = ”def”,此時即S1[i] = S[j]的情況,S1變換爲S2的編輯距離爲d[3, 3] = d[2, 2]。所以得出d[i, j] = d[i – 1, j - 1] + 1。(+1是因爲S1修改了“c”)
4)綜上,得出遞推公式:
=>
5)圖解過程:
通過依次計算出每個子串之間的d[ i ][ j ],最終得到以下的表格
2、C代碼
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define N 660
typedef struct {
char word[64];
char meaning[128];
} node;
node dict[N];//結構體數組,存儲單詞文件
int distance(char *a, char *b) {//形參a,b分別是輸入的單詞和dict數組中的單詞
int i, j, m, n;
m = strlen(a);
n = strlen(b);
int c[m + 1][n + 1];
for (i = 0; i <= m; i++)
c[i][0] = i;
for (j = 1; j <= n; j++)
c[0][j] = j;
for (i = 1; i <= m; i++)
for (j = 1; j <= n; j++) {
int u, v, w;
v = c[i][j - 1] + 1;//對應1中的1)
u = c[i - 1][j] + 1;//對應1中的2)
//對應1中的3)
w = c[i - 1][j - 1];
//取比較的最小值就是當前子串的編輯距離
if (a[i - 1] != b[j - 1])
w++;
//對應1中的3)
if (u > v) u = v;
if (u > w) u = w;
c[i][j] = u;
//取比較的最小值就是當前子串的編輯距離
}
return c[m][n];//返回兩個單詞比較的最終編輯距離
}
void find(char word[64], int dist) {
int mark, i, count;
count = 0;
for (i = 0; i < N; ++i) {//循環讀取dict數組中的單詞約輸入單詞匹配
mark = distance(word, dict[i].word);//返回編輯距離
if (mark <= dist) {//返回值是否和輸入的範圍一致
printf("\t%s\n",dict[i].meaning);
count++;//匹配到的單詞個數
}
}
if (count==0){//沒有匹配到單詞
printf("無匹配單詞!\n");
}
}
void readdict(char dictname[]) {
char item[64];
int i;
FILE *f = fopen(dictname, "r");//讀取文件,模式爲只讀
for (i = 0; i < N; ++i) {
fscanf(f, "%s", dict[i].word);//存入dict結構體數組
while (fscanf(f, "%s", item), isalpha(item[0]) > 0) {//讀取詞組
strcat(dict[i].word, " ");//拼接詞組
strcat(dict[i].word, item);
}
strcpy(dict[i].meaning, item);//單詞解釋
}
fclose(f);
}
int main(int argc, char *argv[]) {
char word[64];
int dist;
if (argc < 1)//判斷命令行參數個數
return 1;
readdict(argv[1]);//讀取單詞文件ps_ec.txt
while (scanf("%s%d", word, &dist) != EOF) {//命令行輸入單詞,以及編輯距離的範圍
find(word, dist);//查找模塊,參數爲單詞,編輯距離範圍,例:word 3,表示匹配和word編輯距離爲0-3的單詞。
}
}
3、相關文件:Github