笔记:代码随想录算法训练营Day65:LeetCode115.不同的子序列\583. 两个字符串的删除操作\72. 编辑距离

学习资料:代码随想录

115.不同的子序列

力扣题目链接

递推公式:求的是个数而不是长度,dp[i-1][j]代表的是用i-2为结尾的s的子序列去能凑出j-1为结尾的t的子序列的方法数,代表的是dp[i][j]的上一状态,当s[i-1]==t[i-1],说明可以从dp[i-1][j-1]的代表的用i-2为结尾的s的子序列去能凑出j-2为结尾的t的子序列的方法数方法数状态各加一个数抵达dp[i][j]代表的方法数的状态,不仅如此,dp[i-1][j]表示i-2为结尾的s就能凑出j-1为结尾的t的方法数,无论s[i-1]==t[i-1]满足不满足,那s以i-1为结尾凑t以j-1为结尾的方法数dp[i][j]肯定包括dp[i-1][j]

简单理解就是:相等时之前就能盛的方法加上刚能盛,,不相等时等于之前能盛的方法

// 定义:dp[i][j]表示以i-1为结尾的s的子序列中出现以j-1为结尾的t的子序列的个数
// 递推公式:求的是个数而不是长度,dp[i-1][j]代表的是用i-2为结尾的s的子序列去能凑出j-1为结尾的t的子序列的方法数,代表的是dp[i][j]的上一状态,当s[i-1]==t[i-1],说明可以从dp[i-1][j-1]的代表的用i-2为结尾的s的子序列去能凑出j-2为结尾的t的子序列的方法数方法数状态各加一个数抵达dp[i][j]代表的方法数的状态,不仅如此,dp[i-1][j]i-2为结尾的s就能凑出j-1为结尾的t的方法数,无论s[i-1]==t[i-1]满足不满足,那s以i-1为结尾凑t以j-1为结尾的方法数dp[i][j]肯定包括dp[i-1][j]
// 初始化:其实还是举例子往前推一两步合适,理解上,dp[i][0]是t的子序列为空字符的状态,所以初始化为1,dp[0][j]是s的子序列为空字符的状态,所以初始化为0. dp[0][0],空字符凑空字符的方法数为0
// 遍历顺序:
// 打印:
class Solution {
public:
    int numDistinct(string s, string t) {
        vector<vector<uint64_t>> dp(s.size()+1,vector<uint64_t>(t.size()+1,0));
        for(int i=0;i<=s.size();i++){
            dp[i][0] = 1;
        }
        for(int j=0;j<=t.size();j++){
            dp[0][j] = 0;
        } 
        dp[0][0] = 1;
        for(int i=1;i<=s.size();i++){
            for(int j=1;j<=t.size();j++){
                if(s[i-1]==t[j-1]){
                    dp[i][j]=dp[i-1][j-1]+dp[i-1][j];
                }
                else dp[i][j]=dp[i-1][j];
            }
        } 
        return dp[s.size()][t.size()];
    }
};

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

力扣题目链接

关于对递推公式的理解上的一点思考:

            for (int j = 1; j <= word2.size(); 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, dp[i][j - 1] + 1);
                }
            }

结合卡哥的讲解,在结合定义理解一下递推公式

dp[i][j]:使word1的以i-1为结尾的子序列和word2的j-1为结尾的子序列相等所需要的最少删减次数

dp[i-1][j]:使word1的以i-2为结尾的子序列和word2的j-1为结尾的子序列相等所需要的最少删减次数,当word[i-1]!=word[j-1],要作删元素的操作了,所以是+1,word1删一个或者word2删一个,dp[i-1][j]和dp[i][j-1]是删之前的最小操作数,

dp[i-1][j-1]+2可以不出现在递推公式里是因为已经包括在前两个里面了

// 定义:dp[i][j]表示使word1的以i-1为结尾的子序列和word2的j-1为结尾的子序列相等所需要的最少删减次数
// 递推公式:还是看word1[i-1]和word2[j-1]是否相等,如果相等的话不用再删了,如果不相等,就需要删了,删一个就可以,从哪个数组中删取决于从哪个数组中删得到的操作步数是最少的,所以要比较一下删i-1(对应dp[i-1][j]+1),还是删j-1(对应dp[i][j-1]+1)
// 初始化:对于dp[i][0],只有全删了才能配上,所以最小操作数是i,dp[j][0]同理,还是找个例子模拟一下合适
// 遍历顺序:看递推公式是什么方向
// 打印
class Solution {
public:
    int minDistance(string word1, string word2) {
        vector<vector<int>> dp(word1.size()+1,vector<int>(word2.size()+1));
        for(int i=0;i<=word1.size();i++){dp[i][0]=i;}     //i=1是,对应word1[0],word1的第一个数
        for(int j=0;j<=word2.size();j++){dp[0][j]=j;}
        for(int i=1;i<=word1.size();i++){
            for(int j=1;j<=word2.size();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,dp[i][j-1]+1);
                }
            }
        }
        return dp[word1.size()][word2.size()];
    }
};

方法二:求最长公共子序列再求需要删减的数量

// 定义:dp[i][j]表示使word1的以i-1为结尾的子序列和word2的j-1为结尾的最长相等子序列
class Solution {
public:
    int minDistance(string word1, string word2) {
        vector<vector<int>> dp(word1.size()+1,vector<int>(word2.size()+1,0));

        for(int i=1;i<=word1.size();i++){
            for(int j=1;j<=word2.size();j++){
                if(word1[i-1]==word2[j-1]){
                    dp[i][j] = dp[i-1][j-1]+1;
                }
                else{
                    dp[i][j] = max(dp[i-1][j],dp[i][j-1]);
                }
            }
        }
        int size = dp[word1.size()][word2.size()];
        int result = 0;
        result = word1.size()+word2.size()-2*size;
        return result;
    }
};

72. 编辑距离

力扣题目链接

这道题能不能不叫编辑距离,叫最小编辑数

递推公式:word1[i-1]==word2[j-1]时就还是等于dp[i][j],不相等时,增操作和减操作体现在递推公式上是一样的,都是删或增i-1,删或增j-1; 替换操作,word1替换word1[i - 1],使其与word2[j - 1]相同,此时不用增删加元素,那么只需要一次替换i-1或j-1的操作,就可以让 word1[i - 1] 和 word2[j - 1] 相同

// 定义:dp[i][j]表示单词word1以i-1为结尾,单词word2以j-1为结尾,两个单词相等需要的最小操作数
// 递推公式:word1[i-1]==word2[j-1]时就还是等于dp[i][j],不相等时,增操作和减操作体现在递推公式上是一样的,都是删或增i-1,删或增j-1; 替换操作,word1替换word1[i - 1],使其与word2[j - 1]相同,此时不用增删加元素,那么只需要一次替换i-1或j-1的操作,就可以让 word1[i - 1] 和 word2[j - 1] 相同
//初始化:dp[i][0]和dp[0][j]还是要分别等于i和j
class Solution {
public:
    int minDistance(string word1, string word2) {
        vector<vector<int>> dp(word1.size()+1,vector<int>(word2.size()+1,0));
        for(int i=0;i<=word1.size();i++){
            dp[i][0] = i;
        }   
        for(int j=0;j<=word2.size();j++){
            dp[0][j] = j;
        }
        for(int i=1;i<=word1.size();i++){
            for(int j=1;j<=word2.size();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[word1.size()][word2.size()];
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值