LeetCode Edit Distance

class Solution {
public:
    int minDistance(string word1, string word2) {
        const int cols = word1.length() + 1;
        const int rows = word2.length() + 1;
        
        int* dp = new int[cols * rows];
    
        int rowbase = 0;
        for (int i=0; i<cols; i++) dp[i] = i;
        for (int i=0; i<rows; i++, rowbase+=cols) dp[rowbase] = i;
        
        rowbase = 0;
        for (int i=1; i<rows; i++) {
            rowbase += cols;
            for (int j=1; j<cols; j++) {
                int diff = word1[j-1] != word2[i-1];
                dp[rowbase + j] = min(min(
                                        dp[rowbase + j-1] + 1, 
                                        dp[rowbase - cols + j] + 1),
                                        dp[rowbase - cols + j-1] + diff);
            }
        }
        
        int ret = dp[cols * rows - 1];
        delete[] dp;
        return ret;
    }
};

跟最长公共子串(LCS)一样是经典的,用动态规划可以解决的问题。举一个例子,要把word1转换到word2

word1 = abc
word2 = ack
dp数组(带标题)
  null a b c
null 0 1 2 3
a 1 0 1 2
c 2 1 1 1
k 3 2 2 2

把word1字符串中的每个字符作为列标题, 把word2字符串中的字符作为行标题,右下角数据部分即为真正的dp数组。

dp[i][j]表示,word2中前i个字符组成的字符串和word1中前j个字符组成的字符串的最小编辑距离。如dp[0][3]即表示word2中一个字符也不取(就是空字符串,表格中用null表示)而word1中取前三个字符(abc)时的最小编辑距离,显然这个距离是3。由word1转换到word2的最小编辑距离和word2到word1的距离是一样的,只不过操作的类型有所不同(相反的)。这里采用和wiki上相同的模式,把目标字符串作为dp数组的列标题,而源字符串作为行标题,定好这个后,再来解释dp数组推导的意义。

首先可以容易的得出dp数组最左边一列和最上边一行的初始值,由一个空字符串变换到一个字符串的编辑距离就是非空字符串的长度。

如果要求dp[i][j],即word2中前i个字符到word1中前j个字符的最小编辑距离,可以从dp[i-1][j], dp[i][j-1], 和dp[i-1][j-1]三个已有的结果进行推导。

  • 1. dp[i-1][j]->dp[i][j]

dp[i-1][j]:word2中前i-1个字符转换为word1中前j个字符的最小编辑距离。对于目前的情况:word2中前i个字符,删掉最后一个字符,取前i-1字符时的解一致了

例子中dp[2][3]表示ac->abc的最小编辑距离,dp[2-1][3]=dp[1][3]即表示a->abc的最小编辑距离,这里为2。现在ac比a多了一个c,又认为前面的a通过两次编辑(因为其编辑距离为2)已经转换变为abc,那么ac->abc只要把这个c删去,剩下的情况就和a->abc一致了,删除是一个操作,所以dp[2][3]的值有可能是dp[1][3]+1

  • 2. dp[i][j-1]->dp[i][j]

类似的,dp[i][j-1],表示word2取前i个字符转换到word1的前j-1个字符所需的最小编辑距离,即word2中的前i个字符已经转换为word1中的前j-1个字符,那么现在word2要转变为word1中的前j个字符,在原来的基础上加一个word1中的第j个字符就得到了word1的前j个字符。

例子中dp[2][3]表示ac->abc的最小编辑距离,dp[2][3-1]=dp[2][2]表示ac->ab的最小编辑距离,这里为1。因为ac已经转换为了ab,现在转变为abc的话就是再加个c,dp[2][3] 的可能值为 dp[2][2] + 1

  • 3. dp[i-1][j-1]->dp[i][j]

源字符串和目标字符串都在原来的基础上增加了一个字符,如果这个字符是相等的那么编辑距离不变,如果两个字符不相等,那么从一个变到另外一个,需要一次替换操作。

最后取上述三者中的最小值作为dp[i][j]所代表情况的最小编辑距离。

参考:

wiki http://en.wikipedia.org/wiki/Levenshtein_distance

zhuli题解 http://www.cnblogs.com/zhuli19901106/p/3568115.html

第二轮,过了一年又忘了,也不得要领,再写一发

 1 class Solution {
 2 public:
 3     int minDistance(string word1, string word2) {
 4         int xlen = word1.size();
 5         int ylen = word2.size();
 6         vector<int> dp1(xlen + 1, 0);
 7         vector<int> dp2(xlen + 1, 0);
 8         
 9         for (int i=1; i<=xlen; i++) {
10             dp1[i] = i;
11         }
12         for (int i=1; i<=ylen; i++) {
13             dp2[0] = i;
14             for (int j=1; j<=xlen; j++) {
15                 dp2[j] = min(dp1[j] + 1, min(dp2[j-1] + 1, dp1[j-1] + (word1[j-1] != word2[i-1])));
16             }
17             swap(dp1, dp2);
18         }
19         return dp1[xlen];
20     }
21 };

 

posted @ 2014-03-18 15:36  卖程序的小歪  阅读(240)  评论(0编辑  收藏  举报