代码随想录算法训练营第五十八天_第九章_动态规划 | 392.判断子序列、115.不同的子序列

LeetCode 392.判断子序列

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

视频讲解https://www.bilibili.com/video/BV1tv4y1B7ym/?spm_id_from=333.788&vd_source=f98f2942b3c4cafea8907a325fc56a48文章讲解https://programmercarl.com/0392.%E5%88%A4%E6%96%AD%E5%AD%90%E5%BA%8F%E5%88%97.html

  • 思路:
    • dp数组含义:
      • dp[i][j]:s的子串[0, i - 1] 作为 t的子串[0, j - 1] 的子序列的长度
      • 注:若s的子串[0, i - 1] 不能作为 t的子串[0, j - 1] 的子序列,则 dp[i][j] = 0
    • 递推公式:
      • s[i - 1] 与 t[j - 1]相同:dp[i][j] = dp[i - 1][j - 1] + 1;
      • s[i - 1] 与 t[j - 1]不同:dp[i][j] = dp[i][j - 1];
      • 注意与LeetCode 1143.最长公共子序列的区别:

        1143如果 text1[i - 1] 与 text2[j - 1] 不同,可以:
        1. text1删末位 与 text2 匹配dp[i -1][j]
        2. text1 与 text2删末位 匹配dp[i][j - 1]
      • 本题只用维护 s 与 t删末位dp[i][j - 1](维护 s删末位 与 t 没有意义,反正是false)
    • 初始化:全0
      • s[0, i-1] 不能作为 空串 的子序列dp[i][0] = 0
      • 空串 作为 t[0, j - 1]的子序列 的长度为0dp[0][j] = 0
    • 遍历顺序:从上到下,从左到右
    • 最终结果:dp[s.size()][t.size()] == s.size()
  • 代码:
// 动态规划:
class Solution {
public:
    bool isSubsequence(string s, string t) {
        vector> dp(s.size() + 1, vector(t.size() + 1, 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 - 1] + 1;
                else dp[i][j] = dp[i][j - 1];
            }
        }
        if (dp[s.size()][t.size()] == s.size()) return true;
        return false;
    }
};
// 时间复杂度:O(n × m)
// 空间复杂度:O(n × m)
// 双指针
class Solution {
public:
    bool isSubsequence(string s, string t) {
        int count = 0;
        for (int i = 0; i < t.size(); ++i) {
            if (s[count] == t[i]) {
                ++count;
            }
        }
        if (count == s.size()) return true;
        return false;
    }
};
// 时间复杂度:O(m)
// 空间复杂度:O(1)

LeetCode 115.不同的子序列

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

代码随想录算法训练营第五十八天_第九章_动态规划 | 392.判断子序列、115.不同的子序列_第1张图片

视频讲解https://www.bilibili.com/video/BV1fG4y1m75Q/?spm_id_from=333.788&vd_source=f98f2942b3c4cafea8907a325fc56a48文章讲解https://programmercarl.com/0115.%E4%B8%8D%E5%90%8C%E7%9A%84%E5%AD%90%E5%BA%8F%E5%88%97.html

  • 思路:
    • dp数组含义:
      • dp[i][j]:s的子串[0, i - 1] 的子序列中 t的子串[0, j - 1] 出现的个数
    • 递推公式:

      代码随想录算法训练营第五十八天_第九章_动态规划 | 392.判断子序列、115.不同的子序列_第2张图片

      • 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];
      • 注意与LeetCode 392.判断子序列的区别:

        392维护 s 与 t删末位dp[i][j - 1](因为需要在 t 中找 s)
      • 本题只用维护 s删末位 与 tdp[i - 1][j](因为需要在 s 中找 t)
    • 初始化:第一列全1,第一行其余为0
      • s[0, i-1] 的子序列中 空串 出现的个数是1dp[i][0] = 1
      • 空串 的子序列中 t[0, j-1] (j > 0) 出现的个数是0dp[0][j] = 0 (j > 0)
    • 遍历顺序:从上到下,从左到右
    • 最终结果:dp[s.size()][t.size()]
  • 代码:
class Solution {
public:
    int numDistinct(string s, string t) {
        vector> dp(s.size() + 1, vector(t.size() + 1, 0));
        // 初始化
        for (int i = 0; i <= s.size(); ++i) {
            dp[i][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()];
    }
};

注:dp数组定义成 int / long long 会溢出;

优化:可将 j <= t.size() 换成 j <= t.size() && j <= i

你可能感兴趣的:(动态规划,leetcode)