题目在这:https://leetcode-cn.com/problems/distinct-subsequences/
思路分析:
这道题是上一道题 392. 判断子序列 的加强版。
这道题不仅要判断是否是子序列,还要找到所有的情况。
示例 1:
输入:s = “rabbbit”, t = “rabbit”
输出:3
解释:
如下图所示, 有 3 种可以从 s 中得到 “rabbit” 的方案。
rabbbit
rabbbit
rabbbit
1.确定dp数组含义:
dp[i][j] 表示s[i-1] 和t[j-1]的子序列个数。
2.确定状态转移公式
-
如果s[i-1] == t[j-1]:
- 则dp[i][j] = dp[i-1][j-1] + dp[i][j-1]
-
如果s[i-1] != t[j-1]:
- 则dp[i][j] = dp[i][j-1]
这里解释一下,之前的基础版本里讲过(链接在上面)和本题思路一致,二维数组中某个格子的左上角是, s串的上一个元素和t串的上一个元素相同情况下的子序列数量,某个格子的左边是 当前s串元素和t串的上一个元素子序列的数量。
别混了,这里相等的时候并不是上一题的格子左上角+1
。因为上一题要求的是子序列的长度,所以当匹配到俩字符串相等元素的时候要加1 ,而这里的dp含义是子序列的数量,当匹配到相等字符的时候,长度加了,但是子序列的数量并不是+1的改变。
注意:这里所说的上一个元素是指从开头到上一个元素的子序列
例如: s:bagg 和 t:bag ,s[3] 和 t[2]是相同的,但是字符串s也可以不用s[3]来匹配,即用s[0]s[1]s[2]组成的bag。
当然也可以用s[3]来匹配,即:s[0]s[1]s[3]组成的bag。
所以当s[i - 1] 与 t[j - 1]相等时,dp[i][j] = 使用当前的元素匹配 + 不使用当前元素匹配(即dp数组当前格子左边格子记录的数),这里所说的当前元素是指t字符串。
所以:
dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j];
当s[i - 1] 与 t[j - 1]不相等时,dp[i][j]只有一部分组成,不用s[i - 1]来匹配,
(即dp数组当前格子左边格子记录的数)
所以:
dp[i - 1][j]
3.初始化dp数组:
对于第一行,s为空集,空集是任何字符串的子集,所以第一行都是1.
同样第一列的t为空集, 空集仅有空集作为子集。所以除了dp[0][0]=1,第一列都是0.
4.确定遍历顺序:
应题目要求,即在S串中挑选字母找T。所以我们拿出来T串中的每一个字母去S中遍历。即外层T循环 内层S循环。
完整代码
class Solution:
def numDistinct(self, s: str, t: str) -> int:
dp =[[0] * (len(s)+1) for _ in range(len(t)+1)]
for _ in range(len(s)+1):
dp[0][_] = 1
for i in range(1,len(t)+1):
for j in range(1,len(s)+1):
if s[j-1] == t[i-1]:
# 相等的两种情况
dp[i][j] = dp[i-1][j-1] + dp[i][j-1]
else:
dp[i][j] = dp[i][j-1]
return dp[-1][-1]