20230704-CodeTop

76 阅读1分钟

leetcode-72 编辑距离

DP双序列典型题,这种题往往都是2维dp,两个维度分别代表两个序列上的位置。这道题的思路就是设dp[i][j]为word1前i个和word2前j个的编辑距离,如果当前两个字符串的字符一致则跳过,否则改增删分别对应一种状态转移,取三者最小值。改对应dp[i-1][j-1]+1,删(删word1)对应dp[i-1][j]+1,增(也是增word1)对应dp[i][j-1]+1。这里增稍微有点不好想,可以理解为增word1等效于删word2。需要注意的细节是,将dp数组开成m+1,n+1是为了便于处理base case,否则在base case就要处理改增删逻辑,很麻烦。

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

leetcode-1143 最长公共子序列

这题和编辑距离很像,同属双序列的DP题。定义dp[i][j]为text1到位置i,text2到位置j为止的最长公共子序列长度。注意这里定义其实不是很好,应该参考上面定义为前i个,从下面代码里能看出这样定义的base case处理很麻烦。状态转移也跟编辑距离很像,当前两个字母相等则dp[i][j] = dp[i - 1][j - 1] + 1,否则dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])。

class Solution {
public:
    int longestCommonSubsequence(string text1, string text2) {
        int m = text1.size(), n = text2.size();
        vector<vector<int>> dp(m, vector<int>(n));
        bool flag = false;
        for(int i = 0; i < m; ++i){
            if(flag){
                dp[i][0] = 1;
                continue;
            }
            if(text1[i] == text2[0]){
                flag = true;
                dp[i][0] = 1;
            }
        }
        flag = false;
        for(int j = 0; j < n; ++j){
            if(flag){
                dp[0][j] = 1;
                continue;
            }
            if(text2[j] == text1[0]){
                flag = true;
                dp[0][j] = 1;
            }
        }
        for(int i = 1; i < m; ++i){
            for(int j = 1; j < n; ++j){
                if(text1[i] == text2[j]){
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                }
                else dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
            }
        }
        return dp[m - 1][n - 1];
    }
};

后面还做了不同路径1、2,最小路径和。这种类型题基本熟了就不记了。需要注意这种题用DP解的前提是不能绕回来,也就是不能出现循环依赖。如果能绕回来则需要用最短路之类的。