_47LeetCode代码随想录算法训练营第四十七天-动态规划 | 392.判断子序列、115.不同的子序列

_47LeetCode代码随想录算法训练营第四十七天-动态规划 | 392.判断子序列、115.不同的子序列

题目列表

  • 392.判断子序列
  • 115.不同的子序列

392.判断子序列

代码随想录地址:https://programmercarl.com/0392.%E5%88%A4%E6%96%AD%E5%AD%90%E5%BA%8F%E5%88%97.html

题目

给定字符串 st ,判断 s 是否为 t 的子序列。

字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace""abcde"的一个子序列,而"aec"不是)。

进阶:

如果有大量输入的 S,称作 S1, S2, … , Sk 其中 k >= 10亿,你需要依次检查它们是否为 T 的子序列。在这种情况下,你会怎样改变代码?

致谢:

特别感谢 @pbrother 添加此问题并且创建所有测试用例。

示例 1:

输入:s = "abc", t = "ahbgdc"
输出:true

示例 2:

输入:s = "axc", t = "ahbgdc"
输出:false

提示:

  • 0 <= s.length <= 100
  • 0 <= t.length <= 1 0 4 10^4 104
  • 两个字符串都只由小写字符组成。

思路

动态规划五部曲:

  1. 确定dp数组(dp table)以及下标的含义

dp[i] [j] 表示以下标i-1为结尾的字符串s,和以下标j-1为结尾的字符串t,相同子序列的长度为dp[i] [j]

  1. 递推公式

if (s[i - 1] == t[j - 1]),那么dp[i] [j] = dp[i - 1] [j - 1] + 1;,因为找到了一个相同的字符,相同子序列长度自然要在dp[i-1] [j-1]的基础上加1。

if (s[i - 1] != t[j - 1]),此时相当于t要删除元素,t如果把当前元素t[j - 1]删除,那么dp[i] [j] 的数值就是 看s[i - 1]与 t[j - 2]的比较结果了,即:dp[i] [j] = dp[i] [j - 1]。

  1. 初始化

全部初始化为0。

  1. 遍历顺序

从上到下从左到右。

  1. 动态推导dp数组

草稿纸。

代码

双指针思路

  • 时间复杂度:O(m + n)

  • 空间复杂度:O(1)

/*
 * @lc app=leetcode.cn id=392 lang=cpp
 *
 * [392] 判断子序列
 */

// @lc code=start
class Solution {
public:
    bool isSubsequence(string s, string t) {
        int m = s.size();
        int n = t.size();
        //双指针的方法,两个指针分别从s和t左往右移
        int i = 0, j = 0;
        while(i < m && j < n)
        {
            if(s[i] == t[j])
            {
                i++;
                j++;
            }
            else
                j++;
        }
        if(i == m)
            return true;
        return false;
    }
};
// @lc code=end

动态规划思路

  • 时间复杂度:O(n × m)
  • 空间复杂度:O(n × m)
/*
 * @lc app=leetcode.cn id=392 lang=cpp
 *
 * [392] 判断子序列
 */

// @lc code=start
class Solution {
public:
    bool isSubsequence(string s, string t) {
        int sSize = s.size();
        int tSize = t.size();
        //定义和初始化dp数组
        vector<vector<int>> dp(sSize + 1, vector<int>(tSize + 1, 0));
        //遍历
        for(int i = 1; i <= sSize; i++)
        {
            for(int j = 1; j <= tSize; j++)
            {
                if(s[i - 1] == t[j - 1])
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                else
                    dp[i][j] = dp[i][j - 1];
            }
        }
        if(dp[sSize][tSize] == sSize)
            return true;
        return false;
    }
};
// @lc code=end

115.不同的子序列

代码随想录地址:https://programmercarl.com/0115.%E4%B8%8D%E5%90%8C%E7%9A%84%E5%AD%90%E5%BA%8F%E5%88%97.html

题目

给定一个字符串 s 和一个字符串 t ,计算在 s 的子序列中 t 出现的个数。

字符串的一个 子序列 是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字符串。(例如,"ACE""ABCDE" 的一个子序列,而 "AEC" 不是)

题目数据保证答案符合 32 位带符号整数范围。

示例 1:

输入:s = "rabbbit", t = "rabbit"
输出:3
解释:
如下图所示, 有 3 种可以从 s 中得到 "rabbit" 的方案。
rabbbit
rabbbit
rabbbit

示例 2:

输入:s = "babgbag", t = "bag"
输出:5
解释:
如下图所示, 有 5 种可以从 s 中得到 "bag" 的方案。 
babgbag
babgbag
babgbag
babgbag
babgbag

提示:

  • 0 <= s.length, t.length <= 1000
  • st 由英文字母组成

思路

动态规划五部曲:

  1. 确定dp数组(dp table)以及下标的含义

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

  1. 确定递推公式

这一类问题,基本是要分析两种情况

  • s[i - 1] 与 t[j - 1]相等
  • s[i - 1] 与 t[j - 1] 不相等

当s[i - 1] 与 t[j - 1]相等时,dp[i] [j]可以有两部分组成。

s[i - 1] [j - 1]:表示以i - 2为结尾的s子序列 出现 以j -2为结尾 的个数

s[i - 1] [j]:表示以i - 2为结尾的s子序列 出现 以j - 1为结尾 的个数

假设字符有重复,那么对于字符串t的组成有两种选择,第一种选择当前的i - 1字符,第二种选择前面的字符;具体存不存在重复字符,交给dp数组来递推表示。

例如: 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]只有一种选择 也就是选择前面的 字符,即:dp[i - 1] [j]。

  1. dp数组初始化

dp[i] [0]:s中的空字符串可以组成一个空字符串,且只有一种情况。所以dp[i] [0] = 1。(i可以等于0)

dp[0] [j]:s中的空字符串不能组成字符串t,所以dp[0] [j] = 0。(j不可以等于0)

  1. 遍历顺序

从上到下从左到右。

  1. dp数组动态推导

在这里插入图片描述

代码

使用二维数组:

  • 时间复杂度:O(m*n),m表示字符串s的长度,n表示字符串t的长度。
  • 空间复杂度:O(m*n)
/*
 * @lc app=leetcode.cn id=115 lang=cpp
 *
 * [115] 不同的子序列
 */

// @lc code=start
class Solution {
public:
    int numDistinct(string s, string t) {
        //定义dp数组 使用int会over flow
        vector<vector<uint64_t>> dp(s.size() + 1, vector<uint64_t>(t.size() + 1));
        //初始化dp数组
        for(int i = 0; i <= s.size(); i++)
            dp[i][0] = 1;
        for(int j = 1; j <= t.size(); j++)
            dp[0][j] = 0;
        //遍历
        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] + dp[i - 1][j - 1];
                else
                    dp[i][j] = dp[i - 1][j];
            }
        return dp[s.size()][t.size()];
    }
};
// @lc code=end

使用一维数组:

  • 时间复杂度:O(m*n),m表示字符串s的长度,n表示字符串t的长度。
  • 空间复杂度:O(n)
/*
 * @lc app=leetcode.cn id=115 lang=cpp
 *
 * [115] 不同的子序列
 */

// @lc code=start
class Solution {
public:
    int numDistinct(string s, string t) {
        //定义dp数组 使用int会over flow
        vector<uint64_t> dp(t.size() + 1);
        //初始化dp数组
        dp[0] = 1;
        for(int i = 1; i <= t.size(); i++)
            dp[i] = 0;
        //遍历
        for(int i = 1; i <= s.size(); i++)
            for(int j = t.size(); j >= 1; j--)
            {
                //当前字符相等的情况
                if(s[i - 1] == t[j - 1])
                    dp[j] = dp[j] + dp[j - 1];
                else
                    dp[j] = dp[j];
            }
        return dp[t.size()];
    }
};
// @lc code=end
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Jasmine-Lily

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

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

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

打赏作者

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

抵扣说明:

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

余额充值