☆打卡算法☆LeetCode 115、 不同的子序列 算法解析

144 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第9天,点击查看活动详情

推荐阅读

大家好,我是小魔龙,Unity3D软件工程师,VR、AR,虚拟仿真方向,不定时更新软件开发技巧,生活感悟,觉得有用记得一键三连哦。

一、题目

1、算法题目

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

题目链接:

来源:力扣(LeetCode)

链接: 115. 不同的子序列

2、题目描述

给定一个字符串 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

二、解题

1、思路分析

这道题可以考虑使用动态规划的方法阶梯,假设字符串s和t的长度为m和n,要算s的子序列在t中出现的个数,那么s的长度一定大于或等于t的长度,也就是只有当m≥n的时候,个数才大于0,如果m≤n,就直接返回0。

创建二维数组dp,其中dp[i][j]表示前 t[i] 个字符可以由 s[j] 字符串组成的个数。

所以动态规划方程: 当 s[j] == t[i] , dp[i][j] = dp[i-1][j-1] + dp[i][j-1]

当s[j] != t[i] , dp[i][j] = dp[i][j-1]

通过动态方程,最终计算得到dp[0][0]即为在s的子序列中t出现的个数。

2、代码实现

代码参考:

class Solution {
    public int numDistinct(String s, String t) {
        int m = s.length(), n = t.length();
        if (m < n) {
            return 0;
        }
        int[][] dp = new int[m + 1][n + 1];
        for (int i = 0; i <= m; i++) {
            dp[i][n] = 1;
        }
        for (int i = m - 1; i >= 0; i--) {
            char sChar = s.charAt(i);
            for (int j = n - 1; j >= 0; j--) {
                char tChar = t.charAt(j);
                if (sChar == tChar) {
                    dp[i][j] = dp[i + 1][j + 1] + dp[i + 1][j];
                } else {
                    dp[i][j] = dp[i + 1][j];
                }
            }
        }
        return dp[0][0];
    }
}

image.png

3、时间复杂度

时间复杂度 : O(mn)

其中m和n分别是字符串s和t的长度,对于二维数组来说,需要对dp中每个元素进行计算。

空间复杂度: O(mn)

其中m和n分别是字符串s和t的长度。

三、总结

题解中的关键:

s[i] == t[j]的时候, s[i] 可以选择自己是否跟 t[j]匹配

  • 如果匹配,那么 dp[i][j] 其中一部分数量就是 dp[i+1][j+1]
  • 如果选择不匹配(这样可以让前面的字符跟t[j]匹配,毕竟t 短的,s 长) dp[i][j] 另外一部分就是 dp[i+1][j]

所以才会有:

dp[i][j] = dp[i+1][j+1] + dp[i+1][j]