算法设计练习3 求字符串转化的最小操作次数 DP动态规划

题目来自leetcode上的动态规划类的练习题, 难度系数为hard。

题目链接:https://leetcode.com/problems/edit-distance/description/

题目要求计算把一个字符串转化成目标字符串的最小操作次数。一开始我想到字符串转化的方法是按相等数目的字符递增地进行转化,这个方法不能有序地解决转化的最小操作次数,所以找不到动态规划的转态转移方程。后来参考discussion中的一个楼主的解题思路(https://leetcode.com/problems/edit-distance/discuss/25846/20ms-Detailed-Explained-C++-Solutions-(O(n)-Space)),发现正确的字符串转化方法应该是对源字符串从前往后按递增顺序连接的字符串进行转化,而每一次转化的目标是目标字符串从前往后按递增顺序连接的字符串。

举个例子就很容易理解,比如要将abcde转化成hijkl。

操作字符串 目标字符串
-(空字符串) - h hi hij ... hijkl
a - h hi hij ... hijkl
ab - h hi hij ... hijkl
....            
abcde - h hi hij ... hijkl
转化次序 1 2 3 4 ... 6

按照这样的转化顺序,算法通过两个for循环即可解决,时间复杂度为O(mn)。同时,在这样的转化顺序下,我们可以找到符合动态规划的状态转移方程。

令dp[i][j]表示把word1的前i个字符转化成word2的前j个字符的最小操作次数。

首先考虑两种简单的特殊情况,当对空字符进行转化和转化成空字符的时候:

dp[0][j] = j;

dp[i][0] = i;

然后对于通常情况,假设我们已经知道dp[i-1][j-1],同样分成两种进行考虑:

当word1[i-1] == word2[j-1]时, 在dp[i-1][j-1]的基础上, 不再需要进行操作,则dp[i][j] = dp[i-1][j-1]

当word1[i-1] != word2[j-1]时,可能进行的操作有插入,删除,替换三种,分三种情况讨论:

插入:假如我们已经知道dp[i][j-1],即字符串word1[0...i-1]能转化成word2[0...j-2],那么只需要在word1[0...i-1]后面插入word2[j-1]即可,所以dp[i][j] = dp[i][j-1] + 1;

删除:类似地, dp[i][j] = dp[i-1][j] + 1;

替换:类似地,dp[i][j] = dp[i-1][j-1] + 1;

到此为止,已经找到了解决该问题的完整的状态转移方程,就可以用dp解决了。

class Solution {
public:
    int minDistance(string word1, string word2) {
        int n = word1.length(), m = word2.length();
        vector > dp(n+1, vector (m+1, 0));
        for(int i = 1; i <= m; i++) 
            dp[0][i] = i;
        for(int i = 1; i <= n; i++)
            dp[i][0] = i;
        for(int i = 1; i <=n; i++) {
            for(int j = 1; j <= m; j++) {
                if(word1[i-1] == word2[j-1])
                    dp[i][j] = dp[i-1][j-1];
                else 
                    dp[i][j] = 1 + min(dp[i][j-1], min(dp[i-1][j], dp[i-1][j-1]));
            }
        }
        return dp[n][m];
    }
};

 

你可能感兴趣的:(学习笔记)