算法训练1-day38-动态规划

22 阅读3分钟
  1. 115. 不同的子序列

dp[i][j]:以i-1为结尾的s子序列中出现以j-1为结尾的t的个数为dp[i][j]

s[i - 1]t[j - 1]相等时,dp[i][j]可以有两部分组成。 一部分是用s[i - 1]来匹配,那么个数为dp[i - 1][j - 1]。即不需要考虑当前s子串和t子串的最后一位字母,所以只需要 dp[i-1][j-1]。 一部分是不用s[i - 1]来匹配,个数为dp[i - 1][j]。 例如: s:bagg 和 t:bag ,s[3]t[2]是相同的,但是字符串s也可以不用s[3]来匹配,即用s[0]s[1]s[2]组成的bag。 当然也可以用s[3]来匹配,即:s[0]s[1]s[3]组成的bag。 所以当s[i - 1]t[j - 1]相等时,dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j];

s[i - 1]t[j - 1]不相等时,dp[i][j]只有一部分组成,不用s[i - 1]来匹配(就是模拟在s中删除这个元素),即:dp[i - 1][j] 所以递推公式为:dp[i][j] = dp[i - 1][j];

AC代码:

class Solution {
public:
    int numDistinct(string s, string t) {
        int n = s.size();
        int m = t.size();

        if (n < m) {
            return 0;
        }
        // 0-i的s的子序列中0-j的t出现的个数
        vector<vector<long long>> dp(n + 1, vector<long long>(m + 1));
        // 初始化第一列
        for (int i = 1; i <= n; i++) {
            if (s[i - 1] == t[0]) {
                dp[i][1] = dp[i - 1][1] + 1;
            } else {
                dp[i][1] = dp[i - 1][1];
            }
        }
        for (int i = 1; i <= n; i++) {
            for (int j = 2; j <= m; ++j) {
                if (s[i - 1] == t[j - 1]) {
	                //题目可能会超范围,不过题目保证答案在int范围内
	                //因此可以用INT_MAX取余来避免溢出
                    dp[i][j] = (dp[i - 1][j - 1] + dp[i - 1][j]) % INT_MAX;
                } else {
                    dp[i][j] = dp[i - 1][j];
                }
            }
        }

        return dp[n][m];
    }
};

  1. 583. 两个字符串的删除操作

dp[i][j]:以i-1为结尾的字符串word1,和以j-1位结尾的字符串word2,想要达到相等,所需要删除元素的最少次数。

注意初始化,dp[i][0]不是全部等于1,而是就等于当前长度,因为j=0代表word2为空串,要变为空串,那么就是全部删除

class Solution {
public:
    int minDistance(string word1, string word2) {
        int n = word1.size();
        int m = word2.size();

        vector<vector<int>> dp(n + 1, vector<int>(m + 1));
        for (int i = 1; i <= n; i++) {
            dp[i][0] = dp[i - 1][0] + 1;
        }
        for (int j = 1; j <= m; ++j) {
            dp[0][j] = dp[0][j - 1] + 1;
        }
        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] = min(dp[i - 1][j], dp[i][j - 1]) + 1;
                }
            }
        }

        return dp[n][m];
    }
};
  1. 72. 编辑距离

583. 两个字符串的删除操作类似,不过多了一个替换操作,于是dp[i][j]就可由两个方向得到:删除(或者说插入,类似的效果)word1[i - 1] 或者 word2[j - 1]字符,即dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + 1;;或是替换当前字符,将word1[i - 1] 替换为 word2[j - 1],或者反过来,即dp[i][j] = min(dp[i][j], dp[i - 1][j - 1] + 1);,取这两种中的最小值

class Solution {
public:
    int minDistance(string word1, string word2) {
        int n = word1.size();
        int m = word2.size();

        vector<vector<int>> dp(n + 1, vector<int>(m + 1));
        for (int i = 1; i <= n; i++) {
            dp[i][0] = dp[i - 1][0] + 1;
        }
        for (int j = 1; j <= m; ++j) {
            dp[0][j] = dp[0][j - 1] + 1;
        }
        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] = min(dp[i - 1][j], dp[i][j - 1]) + 1;
                    // 替换 
                    dp[i][j] = min(dp[i][j], dp[i - 1][j - 1] + 1);
                }
            }
        }

        return dp[n][m];
    }
};