算法学习 | day47/60 判断子序列/不同的子序列

本文介绍了如何使用动态规划解决LeetCode中的两个子序列相关问题:一个是判断给定字符串s是否为字符串t的子序列,另一个是计算字符串t中能形成字符串s的不同子序列的最大数量。作者详细解释了状态定义和转移过程。
摘要由CSDN通过智能技术生成

一、题目打卡

        1.1 判断子序列

        题目链接:. - 力扣(LeetCode)

class Solution {
public:
    bool isSubsequence(string s, string t) {
        vector<vector<int>> dp(t.size() + 1, vector<int>(s.size() + 1, 0));

        for(int i = 1; i < t.size() + 1; i++){
            for(int j = 1 ; j < s.size() + 1; j++){
                if(s[j-1] == t[i-1]) dp[i][j] = dp[i - 1][j - 1] + 1;
                else dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
            }
        }
        // for(int i = 0 ; i <= t.size();i++){
        //     for(int j = 0 ; j <= s.size();j++){
        //         cout << dp[i][j] << " ";
        //     }
        //     cout << endl;
        // }
        return dp[t.size()][s.size()] == s.size();
    }
};

        弄清楚这个题目的状态定义:dp[i][j] 表示以下标i-1为结尾的字符串s,和以下标j-1为结尾的字符串t,相同子序列的长度为dp[i][j]。

        然后状态转移就分当前的两个序列当前值相等和不相等两种情况进行处理,其实这里相等,也可以像背包问题一样理解,也就是取用了这个数字,所以就不能从左和上面继续取值,此时就应该取左上角的状态转移过来,另一个情况则是不取用当前这个值,然后从状态的左边和上面转移而来。

        1.2 不同的子序列

        题目链接:. - 力扣(LeetCode)

class Solution {
public:
    int numDistinct(string t, string s) {
        // vector<vector<int>> dp(s.size() + 1, vector<int>(t.size() + 1, 0));
        vector<vector<unsigned long long>> dp(s.size(), vector<unsigned long long>(t.size(), 0));

        if(s[0] == t[0]) dp[0][0] = 1;
        // 这个是不用初始化的,因为如果s的数量都比t大了,那么是不可能用t组成s的
        // for(int i = 1;i < s.size();i++){
        //     if(s[i] == t[0]) dp[i][0] = dp[i-1][0] + 1;
        //     else dp[i][0] = dp[i-1][0];
        // }

        for(int i = 1 ; i < t.size();i++){
            if(t[i] == s[0]) dp[0][i] = dp[0][i - 1] + 1;
            else dp[0][i] = dp[0][i - 1];
        }

        for(int i = 1; i < s.size();i++){
            for(int j = 1;j < t.size();j++){
                if(s[i] == t[j]) dp[i][j] = dp[i - 1][j - 1] + dp[i][j - 1];
                else dp[i][j] = dp[i][j - 1];
            }
        }
        // for(int i = 0; i < s.size();i++){
        //     for(int j = 0 ; j < t.size();j++){
        //         cout << dp[i][j] << " ";
        //     }
        //     cout << endl;
        // }
        return dp[s.size() - 1][t.size() - 1];
    }
};

        首先还是把dp数组的定义弄明白,如果是我这样定义的话,dp[i][j]就是以下标 i 为结尾的字符串 t 和以下标 j 未结尾的字符串 s,相同子序列出现的最大次数。

        然后画了些图:

        上面基本就是我思路了,这个题目我不咋习惯用i-1索引的方式,虽然这样初始化麻烦了一点,但是我觉得比较好理解,递推的那个过程,其实也是一样的,碰到相等的,那就可以进行新的组合,那么新的组合的前面就应该来自于左上角的部分,而因为这个统计的是个数,所以还应该加上不使用当前这个值所统计的个数,也就是来自左边;另一种情况是,不相等,那么就直接继承左边就可以了。

  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值