pic

Edit Distance

Given two words word1 and word2, find the minimum number of steps required to convert word1 to word2. (each operation is counted as 1 step.) 

You have the following 3 operations permitted on a word: 

 a) Insert a character
 b) Delete a character
 c) Replace a character

分析

题意是寻找两个字符串的最小编辑距离,可以字符可以通过insert、delete、replace三种编辑方法使之相等.

     * 这是典型的DP问题
     * 对字符串A[1,2,...,m]和字符串B[1,2,...,n],长度分别为m,n
     * 1、若A[m]=B[n],那么A[1,...,m]和B[1,...,n]的编辑距离应该等于A[1,...,m-1]和B[1,...,n-1]
     * 2、若A[m]!=B[n],情况就比较复杂:编辑方法分为insert、delete、replace三种。我们需要分别
     *    求出这三种方法的最小值,然后从这三个值中再取最小值。
     *    insert分为对A操作或B操作,所以有两个值,minDistance(A[1,...,m], B[1,...,n-1])和minDistance(A[1,...,m-1], B[1,...,n]) 
     *    delete同样分为对A和B操作,两个值,minDistance(A[1,...,m], B[1,...,n-1])和minDistance(A[1,...,m-1], B[1,...,n]),可以发现与insert具有一样的值 
     *    replace仅有一个值minDistance(A[1,...,m-1], B[1,...,n-1])
     * 3、子问题的求解可以使用自底向上或者自顶向下两种方法,自顶向下递归嵌套出现了超时错误,因此需要使用自底向上求解子问题

Code

Code 1:自顶向下的递归式的解法(Limit Exceeded)

class Solution {
public:
    /*
     * 这是典型的DP问题
     * 对字符串A[1,2,...,m]和字符串B[1,2,...,n],长度分别为m,n
     * 1、若A[m]=B[n],那么A[1,...,m]和B[1,...,n]的编辑距离应该等于A[1,...,m-1]和B[1,...,n-1]
     * 2、若A[m]!=B[n],情况就比较复杂:编辑方法分为insert、delete、replace三种。我们需要分别
     *    求出这三种方法的最小值,然后从这三个值中再取最小值。
     * 3、insert分为对A操作或B操作,所以有两个值,minDistance(A[1,...,m], B[1,...,n-1])和minDistance(A[1,...,m-1], B[1,...,n]) 
     * 4、delete同样分为对A和B操作,两个值,minDistance(A[1,...,m], B[1,...,n-1])和minDistance(A[1,...,m-1], B[1,...,n]),
          可以发现与insert具有一样的值。 
     * 5、replace仅有一个值minDistance(A[1,...,m-1], B[1,...,n-1])
     * 6、下面的解法使用的是自顶向下的递归式解法,多层嵌套,导致了最后的Time Limit Exceeded错误,超时
     */
    int minDistance(string word1, string word2) {
        // IMPORTANT: Please reset any member data you declared, as
        // the same Solution instance will be reused for each test case.
        if(word1.size() == 0)   return word2.size();
        if(word2.size() == 0)   return word1.size();
        int minInsert = 0, minDelete = 0, minReplace = 0;
        if(word1[word1.length() - 1] == word2[word2.length() - 1])
            return minDistance(word1.substr(0, word1.length()-1), 
                word2.substr(0, word2.length() -1));
        else
        {
            //通过insert操作的min距离
            minInsert = 1 + std::min(minDistance(word1, word2.substr(0, word2.length() - 1)), 
                minDistance(word1.substr(0, word1.length()-1), word2));
            /*
            //通过delete操作的min距离
            minDelete = 1 + std::min(minDistance(word1.substr(0, word1.length() -1), word2), 
            minDistance(word1, word2.substr(0, word2.length() - 1)) );
            */
            //通过replace操作的min距离
            minReplace = 1 + minDistance(word1.substr(0, word1.length() - 1), word2.substr(0, word2.length() - 1));
            /*
             *返回值,应该是minInsert、minDelete、minReplace三个的最小值
             *通过观察,minInsert=minDelete,所以我们可以省事,最小值可以只比较两个
             */
            return std::min(minInsert, minReplace);
        }
    }
};

Code 2:自底向上的非递归式的解法(Accepted)

class Solution {
public:
    /* 这是典型的DP问题
     * 对字符串A[1,2,...,m]和字符串B[1,2,...,n],长度分别为m,n
     * 1、若A[m]=B[n],那么A[1,...,m]和B[1,...,n]的编辑距离应该等于A[1,...,m-1]和B[1,...,n-1]
     * 2、若A[m]!=B[n],情况就比较复杂:编辑方法分为insert、delete、replace三种。我们需要分别
     *    求出这三种方法的最小值,然后从这三个值中再取最小值。
     * 3、insert分为对A操作或B操作,所以有两个值,minDistance(A[1,...,m], B[1,...,n-1])和minDistance(A[1,...,m-1], B[1,...,n]) 
     * 4、delete同样分为对A和B操作,两个值,minDistance(A[1,...,m], B[1,...,n-1])和minDistance(A[1,...,m-1], B[1,...,n]),
          可以发现与insert具有一样的值 
     * 5、replace仅有一个值minDistance(A[1,...,m-1], B[1,...,n-1])
     * 6、之前使用自顶向下的递归式方法超时了,下面代码使用了自底向上的方法,避免了递归嵌套,AC
     */
    int minDistance(string word1, string word2) {
        // IMPORTANT: Please reset any member data you declared, as
        // the same Solution instance will be reused for each test case.
        int row = word1.size();
        int col = word2.size();
        int minInsert, minDelete, minReplace;
        if(row == 0)       return col;
        if(col == 0)       return row;
        vector< vector<int> > tables(row + 1, vector<int>(col + 1, 0));
        //设置边界值。即A[j]和B[0]之间的距离应该是j
        for(int rowidx = 0; rowidx < row + 1; rowidx++)
            tables[rowidx][0] = rowidx;
        //设置边界值
        for(int colidx = 0; colidx < col + 1; colidx++)
            tables[0][colidx] = colidx;
        for(int rowidx = 1; rowidx < row + 1; rowidx++)
            for(int colidx = 1; colidx < col + 1; colidx++)
            {
                if(word1[rowidx - 1] == word2[colidx - 1])
                {
                    tables[rowidx][colidx] = tables[rowidx-1][colidx-1];
                }
                else
                {
                    //通过insert操作的min距离
                    minInsert = 1 + std::min(tables[rowidx-1][colidx], tables[rowidx][colidx-1]);
                    //通过delete操作的min距离(和minInsert相等,所以可以不计算)
                    /*
                    minDelete = 1 + std::min(tables[rowidx-1][colidx], tables[tables[rowidx][colidx-1]]);
                    */
                    //通过replace操作的min距离
                    minReplace = 1 + tables[rowidx-1][colidx-1];
                    /*
                     * tables[rowidx][colidx]应该是minInsert、minDelete、minReplace三个值的最小值
                     * 而通过观察可以看到minInsert和minDelete是相等的,所以可以省一次比较
                     */
                     tables[rowidx][colidx] = std::min(minInsert, minReplace);
                }
            }
        return tables[row][col];
    }
};

pic