最短編輯距離


場景

在搜索引擎項目中,我用到了最短編輯距離算法,用於對用戶輸入的查詢進行糾錯,從而優化查詢結果。比如說,我們在輸入英文單詞的時候,由於疏忽或者記憶不准確,會有拼寫錯誤的情況。以單詞beau tiful 為例,假設我們在搜索引擎中輸入beau itful(我故意拼錯了),看看會發生什么。
 
如下圖所示,雖然我把這個單詞拼錯了,但是查詢結果提示“including results for beautiful”,也就是說,它似乎知道我的查詢輸入拼寫錯誤,並根據某種算法,給我推薦了一個與之最近似的單詞(大概率就是本應正確拼寫的單詞)。在這里,就用到了最短編輯距離算法。這個場景,也稱為模糊搜索。

 最短編輯距離

什么是最短編輯距離呢?假定有兩個字符串s1和s2,允許對字符串進行以下三種操作:

1. 插入一個字符

2. 刪除一個字符

3. 替換一個字符

將字符串s1轉換成字符串s2的最少操作次數就是字符串s1和字符串s2之間的最短編輯距離。兩個字符串的最短編輯距離越短,意味着兩個字符串越相似。
 
例1 :s1 = "geek",s2 = "gesek"
我們只需要在s1中插入一個字符,就可以把s1轉換為s2,因此,這兩個字符串的最短編輯距離就是1
 
例2:s1 = "cat",s2 = "cut"
我們只需要在s1中替換一個字符,就可以把s1轉換為s2,因此,這兩個字符串的最短編輯距離就是1
 
例3:s1 = "sunday",s2 = "saturday"
由於第1個字符和最后3個字符是一樣的,因此,我們只要考慮“un”和"atur"即可。首先,把'n'替換成'r',再插入'a'、't',因此最短編輯距離是3
 
以上面例3進行說明,我們 從字符串的最后一位開始,從右向左進行比較,由於最后一位都是'y',因此,不需要任何操作,也就是說,兩者的最短編輯距離等價於"sunda"和"saturda"的最短編輯距離,即d("sunday", "saturday") = d("sunda", "saturda")。因此,如果在比較的過程中遇到了相同的字符,那么二者的最短編輯距離就等價於除了這個字符之外,剩余字符的最短編輯距離,即d(i, j) = d(i-1, j-1)。
 
如果比較的字符不一致,比方說,已經比較到了"sun"和"satur",根據允許的操作,我們有以下3種操作:
(1)插入:在s1末尾插入一個字符'r'(即"sunr"),由於此時末尾字符都是'r',因此就變成了比較"sun"和"satu"的編輯距離,即d("sun", "satur") = d("sun", "satu") + 1,也可以寫成d(i, j) = d(i, j-1) + 1。+1 表示當前進行了一次字符操作。
(2)刪除:刪除s1的最后一個字符,並考察s1剩下的部分與s2的距離。即d("sun", "satur") = d("su", "satur") + 1,也可以寫成d(i, j) = d(i-1, j) + 1。
(3)替換:把s1的最后一個字符替換為s2的最后一個字符,即變成了"sur",因此即d("sun", "satur") = d("su", "satu") + 1,也可以寫成d(i, j) = d(i-1, j-1) + 1。
 
基於上述分析,我們就可以很快寫出遞歸的代碼。如下:
static int min(int x,int y,int z)
    {
        if (x<=y && x<=z) return x;
        if (y<=x && y<=z) return y;
        else return z;
    }
 
static int editDist(String str1 , String str2 , int m ,int n)
    {
        // If first string is empty, the only option is to
    // insert all characters of second string into first
    if (m == 0) return n;
       
    // If second string is empty, the only option is to
    // remove all characters of first string
    if (n == 0) return m;
       
    // If last characters of two strings are same, nothing
    // much to do. Ignore last characters and get count for
    // remaining strings.
    if (str1.charAt(m-1) == str2.charAt(n-1))
        return editDist(str1, str2, m-1, n-1);
       
    // If last characters are not same, consider all three
    // operations on last character of first string, recursively
    // compute minimum cost for all three operations and take
    // minimum of three values.
    return 1 + min ( editDist(str1,  str2, m, n-1),    // Insert
                     editDist(str1,  str2, m-1, n),   // Remove
                     editDist(str1,  str2, m-1, n-1) // Replace                     
                   );
    }

但是我們都知道遞歸會存在大量的重復計算,因此,顯然不是最優解。在這里,我們可以利用動態規划的思想來進行優化。

 
假設dp[i][j]表示s1[i]與s2[j]的最短編輯距離,根據之前的分析,可以寫出如下代碼:
static int editDistDP(String str1, String str2, int m, int n)
    {
        // Create a table to store results of subproblems
        int dp[][] = new int[m+1][n+1];
       
        // Fill d[][] in bottom up manner
        for (int i=0; i<=m; i++)
        {
            for (int j=0; j<=n; j++)
            {
                // If first string is empty, only option is to
                // insert all characters of second string
                if (i==0)
                    dp[i][j] = j;  // Min. operations = j
       
                // If second string is empty, only option is to
                // remove all characters of second string
                else if (j==0)
                    dp[i][j] = i; // Min. operations = i
       
                // If last characters are same, ignore last char
                // and recur for remaining string
                else if (str1.charAt(i-1) == str2.charAt(j-1))
                    dp[i][j] = dp[i-1][j-1];
       
                // If the last character is different, consider all
                // possibilities and find the minimum
                else
                    dp[i][j] = 1 + min(dp[i][j-1],  // Insert
                                       dp[i-1][j],  // Remove
                                       dp[i-1][j-1]); // Replace
            }
        }
   
        return dp[m][n];
    }

時間復雜度:O(m*n)

空間復雜度:O(m*n)
 
參考:
 
 
 


免責聲明!

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



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