不同的子序列-java

文章讲述了如何通过动态规划方法解决字符串s中的子序列与给定字符串t的匹配次数问题,定义了状态数组dp并详细阐述了状态转移规则,以高效地计算出结果,时间复杂度为O(mn)。
摘要由CSDN通过智能技术生成
  • 题目描述(力扣题库115):

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

      示例 1:

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

    • 动态规划 : 

    • 当解决动态规划问题时,通常需要定义一个状态数组来存储中间结果。在这个问题中,我们使用二维数组 `dp` 来表示状态。`dp[i][j]` 表示在字符串 `s` 的子串 `s[i:]` 中出现字符串 `t` 的子串 `t[j:]` 的次数。

      首先,我们从字符串的末尾开始向前遍历。这是因为我们需要依赖后面的状态来计算当前状态。我们将 `dp[i][t.length()]` 初始化为 1,因为对于任何非空字符串 `s`,其末尾与空字符串匹配的次数都是 1。

      然后,我们从 `s` 和 `t` 的末尾开始逐个字符进行比较。如果 `s[i]` 与 `t[j]` 相等,则意味着我们有两种选择:

      1. 保留 `s[i]`,并且尝试匹配 `s[i + 1:]` 和 `t[j + 1:]`,所以此时的状态转移方程为 `dp[i][j] = dp[i + 1][j + 1]`。
      2. 匹配 `s[i]` 和 `t[j]`,然后尝试匹配 `s[i + 1:]` 和 `t[j:]`,所以此时的状态转移方程为 `dp[i][j] += dp[i + 1][j]`。

      如果 `s[i]` 与 `t[j]` 不相等,则意味着我们不能匹配当前字符,只能选择保留 `s[i]` 并尝试匹配 `s[i + 1:]` 和 `t[j:]`,所以状态转移方程为 `dp[i][j] = dp[i + 1][j]`。

      通过不断地更新状态数组 `dp`,最终我们就可以得到整个字符串 `s` 和 `t` 的子序列匹配次数,即 `dp[0][0]`。

      这种动态规划的解法利用了子问题的重叠性质,避免了重复计算,从而实现了高效的解决方案。

  • 解题步骤: 

  1. 初始化状态数组

    • 创建一个二维数组 dp,其大小为 (s.length() + 1) × (t.length() + 1)
    • 初始化 dp[i][t.length()] = 1,表示任何非空字符串 s 的末尾与空字符串匹配的次数都是 1。
  2. 逆向遍历字符串

    • 从 s 和 t 的末尾开始向前遍历字符。
    • 遍历的范围是 i = s.length() - 1 到 0,以及 j = t.length() - 1 到 0
  3. 状态转移

    • 对于当前位置 (i, j),判断 s[i] 和 t[j] 是否相等:
      • 如果相等,则有两种选择:
        • 保留 s[i],并尝试匹配 s[i + 1:] 和 t[j + 1:],更新状态:dp[i][j] = dp[i + 1][j + 1]
        • 匹配 s[i] 和 t[j],并尝试匹配 s[i + 1:] 和 t[j:],更新状态:dp[i][j] += dp[i + 1][j]
      • 如果不相等,则只能保留 s[i],并尝试匹配 s[i + 1:] 和 t[j:],更新状态:dp[i][j] = dp[i + 1][j]
  4. 返回结果

    最终的结果存储在 dp[0][0] 中,表示整个字符串 s 和 t 的子序列匹配次数。

通过以上步骤,可以得到字符串 s 和 t 的子序列匹配次数。这个算法的时间复杂度为 O(mn),其中 m 是字符串 s 的长度,n 是字符串 t 的长度。

  • 以下是代码实现: 
    • class Solution {
          public int numDistinct(String s, String t){
              if(s.length() < t.length()) return 0;
      
              int[][] dp = new int[s.length() + 1][t.length() + 1];
              for (int i = 0; i <= s.length(); i++) {
                  dp[i][t.length()] = 1;
              }
      
              for (int i = s.length() - 1; i >= 0; i--) {
                  for (int j = t.length() - 1; j >= 0; j--) {
                      if(s.charAt(i) == t.charAt(j))
                          dp[i][j] = dp[i + 1][j + 1] + dp[i + 1][j];
                      else
                          dp[i][j] = dp[i + 1][j];
                  }
              }
              return dp[0][0];
          }
      }

  

  •                       以上是本篇文章的全部内容, 感谢观看
  • 39
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值