力扣第115题 不同的子序列 c++ 动态规划 注释版 + Java代码

题目

115. 不同的子序列

困难

相关标签

字符串   动态规划

给你两个字符串 s 和 t ,统计并返回在 s 的 子序列 中 t 出现的个数,结果需要对 109 + 7 取模。

示例 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

提示:

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

思路和解题方法

  1. 创建dp数组:在这段代码中,我们使用了一个二维的dp数组来保存子问题的解。数组的大小是(s.size() + 1)×(t.size() + 1),其中s.size()表示字符串s的长度,t.size()表示字符串t的长度。所以dp数组的行数是s的长度加1,列数是t的长度加1。

  2. 初始化边界条件:在动态规划的问题中,通常需要初始化一些特殊情况下的解。在这段代码中,我们初始化了dp数组的第一行和第一列。根据题目要求,当t为空串时,s中只有一个空串与之匹配,所以dp[i][0]的初始值都是1。另外,当s为空串时,任何非空的t都无法与之匹配,所以dp[0][j]的初始值都是0,其中j从1到t的长度。

  3. 填充dp数组:通过两层循环遍历dp数组,我们可以逐步计算出dp[i][j]的值。循环中的i表示s的索引,j表示t的索引。在每次迭代中,我们比较s[i-1]和t[j-1]的字符:

    • 如果s[i-1]等于t[j-1],说明当前字符能够匹配,此时有两种选择:

      1. 使用s[i-1]匹配t[j-1]:即dp[i][j] = dp[i-1][j-1],表示之前的方案数加上这种选择。
      2. 不使用s[i-1]匹配t[j-1]:即dp[i][j] += dp[i-1][j],表示继承之前的方案数。
    • 如果s[i-1]不等于t[j-1],说明当前字符不能够匹配,此时只能不使用s[i-1],故有dp[i][j] = dp[i-1][j]。

  4. 返回结果:最后,我们返回dp[s.size()][t.size()],即s中有多少个子序列与t相同的方案数。

复杂度

        时间复杂度:

                O(n*m)

时间复杂度:

  • 遍历两个字符串,共需执行 s.size() * t.size() 次循环。
  • 在每个循环中,进行常数时间(O(1))的比较和赋值操作。 所以,总体时间复杂度为 O(s.size() * t.size())。

        空间复杂度

                O(n*m)

空间复杂度:

        使用了一个二维数组 dp,大小为 (s.size() + 1) * (t.size() + 1),空间复杂度为 O(s.size() * t.size())。

c++ 代码

int numDistinct(string s, string t) {
    // 创建一个二维数组 dp,用于存储状态转移值
    vector> dp(s.size() + 1, vector(t.size() + 1));
    
    // 初始化边界条件
    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 - 1] + dp[i - 1][j];
            } else {
                dp[i][j] = dp[i - 1][j];
            }
        }
    }
    
    // 返回最后一个状态转移值,即字符串 t 在字符串 s 中出现的不同子序列的个数
    return dp[s.size()][t.size()];
}

Java代码

class Solution {
    public int numDistinct(String s, String t) {
        // 创建一个二维数组dp,大小为(s.length() + 1) * (t.length() + 1)
        int[][] dp = new int[s.length() + 1][t.length() + 1];
        
        // 初始化边界条件,当t为空字符串时,s中的任意子串都可以匹配,所以dp[i][0] = 1
        for (int i = 0; i < s.length() + 1; i++) {
            dp[i][0] = 1;
        }
        
        // 动态规划计算dp数组的值
        for (int i = 1; i < s.length() + 1; i++) {
            for (int j = 1; j < t.length() + 1; j++) {
                // 如果当前s的字符与t的字符相等,说明可以选择匹配或者不匹配
                if (s.charAt(i - 1) == t.charAt(j - 1)) {
                    // 若选择匹配,则需要找到前面子串的匹配数,即dp[i - 1][j - 1]
                    // 若选择不匹配,则需要找到前面子串的匹配数,即dp[i - 1][j]
                    dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j];
                } else {
                    // 如果当前s的字符与t的字符不相等,则无法匹配,所以匹配数与前面子串相同,即dp[i - 1][j]
                    dp[i][j] = dp[i - 1][j];
                }
            }
        }
        
        // 返回dp数组右下角的值,即整个s和t的匹配数
        return dp[s.length()][t.length()];
    }
}

觉得有用的话可以点点赞,支持一下。

如果愿意的话关注一下。会对你有更多的帮助。

每天都会不定时更新哦  >人<  。

你可能感兴趣的:(leetcode,动态规划,数据结构,leetcode,c++,算法,动态规划,Java)