基於編輯距離的單詞匹配算法

編輯距離單詞匹配算法

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章