2021.3.17
https://leetcode-cn.com/problems/distinct-subsequences/

题目

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

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

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

  1. 输入:s = "rabbbit", t = "rabbit"
  2. 输出:3
  3. 解释:
  4. 如下图所示, 3 种可以从 s 中得到 "rabbit" 的方案。
  5. (上箭头符号 ^ 表示选取的字母)
  6. rabbbit
  7. ^^^^ ^^
  8. rabbbit
  9. ^^ ^^^^
  10. rabbbit
  11. ^^^ ^^^
  12. 输入:s = "babgbag", t = "bag"
  13. 输出:5
  14. 解释:
  15. 如下图所示, 5 种可以从 s 中得到 "bag" 的方案。
  16. (上箭头符号 ^ 表示选取的字母)
  17. babgbag
  18. ^^ ^
  19. babgbag
  20. ^^ ^
  21. babgbag
  22. ^ ^^
  23. babgbag
  24. ^ ^^
  25. babgbag
  26. ^^^

提示:

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

    思路

    知道是动态规划,没想出来,看的官方题解,给的是倒推的方程,理解后写了正推的版本,比倒推快一点。
    dp[i][j] 表示s以i结尾的字符串中包含t以j结尾的字符串的个数
    当 s[i] == t[j] 时

  • s[i]和t[j]匹配,此时个数有 dp[i - 1][j - 1]个

  • s[i]不和t[j]匹配,此时个数有 dp[i - 1][j] 个
  • 所以 dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j]

当 s[i] != t[j]时,只有用S前面的字符串去计算

  • 所以dp[i][j] = dp[i - 1][j]

**
image.png
具体推导图如上所示。两个注意点

  • 如果t是空字符串,那么所有的dp[0-n][0] = 1
  • 如果s是空字符串,那么所有的dp[0][1-m] = 0
  • n是字符串S的长度,m是字符串T的长度

    AC代码

    * 正推版本,比倒推快
    * 执行用时:4 ms, 在所有 Java 提交中击败了82.62%的用户
    * 内存消耗:36.7 MB, 在所有 Java 提交中击败了71.39%的用户
    * @param s
    * @param t
    * @return
    */
    public int numDistinct2(String s, String t) {
    
      int n = s.length();
      int m = t.length();
    
      if (n < m) {
          return 0;
      }
    
      int[][] f = new int[n + 1][m + 1];
      for (int i = 0; i <= n ; i++) {
          f[i][0] = 1;
      }
      for (int i = 1; i <= m; i++) {
          f[0][i] = 0;
      }
    
      for (int i = 1; i <= n ; i++) {
          for (int j = 1; j <= m; j++) {
              f[i][j] = f[i - 1][j];
              if (s.charAt(i - 1) == t.charAt(j - 1)) {
                  f[i][j] += f[i -1][j - 1];
              }
          }
      }
    
      return f[n][m];
    }