115. 不同的子序列

题目

给定一个字符串 s 和一个字符串 t ,计算在 s 的子序列中 t 出现的个数。
字符串的一个 子序列 是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字符串。(例如,"ACE""ABCDE" 的一个子序列,而 "AEC" 不是)
题目数据保证答案符合 32 位带符号整数范围。
示例 1:

  1. 输入:s = "rabbbit", t = "rabbit"
  2. 输出:3
  3. 解释:
  4. 如下图所示, 3 种可以从 s 中得到 "rabbit" 的方案。
  5. (上箭头符号 ^ 表示选取的字母)
  6. rabbbit
  7. ^^^^ ^^
  8. rabbbit
  9. ^^ ^^^^
  10. rabbbit
  11. ^^^ ^^^

示例 2:

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

提示:

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

题解

首先我们可以从 s中除去不在 t 中的字符,例如 s = "aabbcc", t = "ab",那我们把字符 c 删除是不影响结果的。
然后就是进行动态规划,假设 t[0]s 中出现的位置为 i1,i2,...,im,那么有
f(s,t) = f(s[i1+1:],t[1:]) + f(s[i2+1:],t[1:]) +...+ f(s[im+1:],t[1:])
举个很简单的例子:

 f(cbabgbag,bag)
=f(babgbag,bag)
=f(abgbag,ag) + f(gbag,ag) + f(ag,ag)
=f(abgag,ag) + 1 + 1
=f(bgag,g) + f(g,g) + 2
=f(gg,g) + 1 + 2
=2 + 3
=5

那么代码就很容易写出来了:

from functools import lru_cache

def clr(s, t):
    return "".join([c for c in s if c in t])

class Solution:
    @lru_cache(maxsize=None)
    def f(self, s, t):
        count = 0
        if len(s) < len(t):
            return 0
        if len(t) == 1:
            return len(s)
        for i in range(len(s)):
            if s[i] == t[0]:
                count += self.f(clr(s[i+1:], t[1:]), t[1:])
        return count

    def numDistinct(self, s: str, t: str) -> int:
        S = clr(s, t)
        return self.f(S, t)

官方题解

官方题解是这样写状态转移方程的:
f(s,t) = f(s[i+1:],t[1:]) + f(s[i+1:],t)
那么计算方式就变成了这样:

 f(cbabgbag,bag)
=f(babgbag,bag)
=f(abgbag,ag) + f(abgbag,bag)
=f(abgag,ag) + f(abgbag,bag)
=( f(bgag,g) + f(bgag,ag) ) + ( f(gbag,ag) + f(gbag,bag) )
=2 + 1 + 1 + 1
=5

循环程序如下:

class Solution:
    def numDistinct(self, s: str, t: str) -> int:
        m, n = len(s), len(t)
        if m < n:
            return 0

        dp = [[0] * (n + 1) for _ in range(m + 1)]
        for i in range(m + 1):
            dp[i][n] = 1

        for i in range(m - 1, -1, -1):
            for j in range(n - 1, -1, -1):
                if s[i] == t[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]

如果按这种思路写的话,搜索形式如下:

from functools import lru_cache

def clr(s, t):
    return "".join([c for c in s if c in t])

class Solution:
    @lru_cache(maxsize=None)
    def f(self, s, t):
        if len(s) < len(t):
            return 0
        if len(t) == 1:
            return len(s)
        for i in range(len(s)):
            if s[i] == t[0]:
                return self.f(clr(s[i+1:], t[1:]), t[1:])+self.f(s[i+1:], t)
        return 0

    def numDistinct(self, s: str, t: str) -> int:
        S = clr(s, t)
        return self.f(S, t)

不知道为什么速度快了很多。。。按理说我的那个应该更快才对