【dp】不同的子序列 & 两个字符串的删除操作 & 编辑距离

115. 不同的子序列

在这里插入图片描述

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

需要开辟m+1行,n+1列的二维数组


为啥状态方程是:

  • s[i] == t[j] 时 dp[i][j] = dp[i-1][j-1] + dp[i-1][j]

  • s[i] != t[j] 时 dp[i][j] = dp[i-1][j]

先看s[i] == t[j] 时,以s = “rara” t = “ra” 为例,当i = 3, j = 1时,s[i] == t[j]
此时分为2种情况:s串用最后一位的a 以及 不用最后一位的a

  • 如果用s串最后一位的a,那么t串最后一位的a也被消耗掉,此时的子序列其实=dp[i-1][j-1]

  • 如果不用s串最后一位的a,那就得看"rar"里面是否有"ra"子序列的了,就是dp[i-1][j]

所以 dp[i][j] = dp[i-1][j-1] + dp[i-1][j]

再看s[i] != t[j] 比如 s = “rarb” t = “ra” 还是当i = 3, j = 1时,s[i] != t[j]

此时显然最后的b想用也用不上啊。所以只能指望前面的"rar"里面是否有能匹配"ra"的

所以此时dp[i][j] = dp[i-1][j]


如果当前字符s[i - 1]和 t[i - 1]相等,则可以使用当前字符匹配,并使用s[0]~s[i - 2]匹配t[0]~t[j - 2],即dp[i - 1][j - 1]。还可以不使用当前字符s[i - 1]匹配,使用s[0]~s[i - 2]匹配t[0]~t[j - 1],即dp[i - 1][j]

如果当前字符s[i - 1]和 t[i - 1]不相等,则不能使用当前字符s[i - 1]匹配,需要使用s[0]~s[i-2]匹配t[0]~t[j-1],即dp[i-1][j]

在这里插入图片描述

class Solution {
public:
    int numDistinct(string s, string t) {
        int m = s.size();
        int n = t.size();
        // dp[i][j]:以i-1为结尾的s子序列中出现以j-1为结尾的t的个数
        vector<vector<uint64_t>> dp(m + 1, vector<uint64_t>(n + 1));
        // 空字符串出现在空字符串的次数
        dp[0][0] = 1;
        // t为空,空字符串出现在s中的次数
        for (int i = 1; i <= m; i++) dp[i][0] = 1;
        // s为空,t出现在空字符串中的次数
        for (int j = 1; j <= n; j++) dp[0][j] = 0;

        for (int i = 1; i <= m; i++) {
            for (int j = 1; j <= n; j++) {
                if (s[i - 1] == t[j - 1]) {
                    // 当前字符相等
                    // 用当前字符匹配,即使用s[0 ~ i-2]匹配t[0 ~ j-2] dp[i - 1][j - 1]
                    // 不用当前字符匹配,即使用s[0 ~ i-2]匹配t[0 ~ j-1] dp[i - 1][j]
                    dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j];
                } else {
                    // 不用当前字符匹配,即使用s[0 ~ i-2]匹配t[0 ~ j-1] dp[i - 1][j]
                    dp[i][j] = dp[i - 1][j];
                }
            }
        }
        return dp[m][n];
    }
};

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

在这里插入图片描述

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

在这里插入图片描述

class Solution {
public:
    int minDistance(string word1, string word2) {
        // dp[i][j]:以i-1为结尾的字符串word1,和以j-1位结尾的字符串word2,想要达到相等,所需要删除元素的最少次数
        // dp[i][j] = dp[i - 1][j - 1];
        // dp[i][j] = min({dp[i - 1][j - 1] + 2, dp[i - 1][j] + 1, dp[i][j - 1] + 1});
        int m = word1.size();
        int n = word2.size();
        vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));
        dp[0][0] = 0;                              // word1、word2都为空串,删除0次相同
        for(int i = 1; i <= m; i++) dp[i][0] = i;  // word2为空串,word1[0~i-1]变成空串,需要删除i次
        for(int j = 1; j <= n; j++) dp[0][j] = j;  // word1为空串,word2[0~j-1]变成空串,需要删除j次
        for(int i = 1; i <= m; i++){
            for(int j = 1; j <= n; j++){
                if(word1[i - 1] == word2[j - 1]){
                	// 如果当前字符相等,则当前字符不用删除,word1[0] ~ word1[i - 1]和word2[0] ~ word2[j - 1]删除dp[i][j]次后相同
                	// 与word1[0] ~ word1[i - 12]和word2[0] ~ word2[j - 2]删除dp[i - 1][j - 1]次后相同
                	// 这两个删除次数应该相同
                    dp[i][j] = dp[i - 1][j - 1];
                }else{
                	// 若当前字符不相同,则要么是当前的两个字符都删除,变成word1[0] ~ word1[i - 2]和word2[0] ~ word2[j - 2]
                	// 要么是删除word1[i - 1],要么是word2[j - 1]
                	// 三个取最小值即可
                    dp[i][j] = min(dp[i - 1][j - 1] + 2, min(dp[i - 1][j] + 1, dp[i][j - 1] + 1));
                }
            }
        }
        return dp[m][n];
    }
};

72. 编辑距离

在这里插入图片描述

class Solution {
public:
    int minDistance(string word1, string word2) {
        int m = word1.size();
        int n = word2.size();
        vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));
        dp[0][0] = 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], dp[i][j - 1], dp[i - 1][j - 1]}) + 1;
                }
            }
        }
        return dp[m][n];
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bugcoder-9905

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值