题目
给定一个字符串 S 和一个字符串 T,计算在 S 的子序列中 T 出现的个数。
一个字符串的一个子序列是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字符串。(例如,“ACE” 是 “ABCDE” 的一个子序列,而 “AEC” 不是)
示例 1:
输入: S = “rabbbit”, T = “rabbit”
输出: 3
解释:
如下图所示, 有 3 种可以从 S 中得到 “rabbit” 的方案。
(上箭头符号 ^ 表示选取的字母)
rabbbit
^^^^ ^^
rabbbit
^^ ^^^^
rabbbit
^^^ ^^^
思路
可以借助最长公共子序列的思路,最长公共子序列是找到字符串s1
与字符串s2
最长的公共部分,而本题可以将公共部分定死,就是T
这个串。
首先定义一个二维数组a[lt][ls]
,其中lt
是串T的长度,ls
是串S的长度。a[i][j]
表示在S[0 ~ j]
中,等于T[0 ~ i]
的子串共有多少种组合。
分析a[i][j]
- 当
T[i] != S[j]
时,a[i][j]
的值就等于a[i][j-1]
的值,即a[i][j] = a[i][j - 1]
,好理解的,因为此时S[0 ~ j]
中等于T[0 ~ i]
的子串个数,等于S[0 ~ j-1]
中等于T[0 ~ i]
的子串个数。 - 当
T[i] == S[j]
时,我们要考虑两个值。- 第一,
a[i][j - 1]
的值,先看下它的意义:S[0 ~ j-1]
中等于T[0 ~ i]
的子串个数。而我们要求的是S[0 ~ j]
中等于T[0 ~ i]
的子串个数, 它前面的S[0 ~ j-1]
中已经包含了a[i][j-1]
个T[0 ~ i]
, 此时再一次T[i] == S[j]
,倘若把T[i]
这个字符换成当前的S[j]
,不又是a[i][j-1]
种吗?所以,此时定有a[i][j] >= a[i][j - 1]
。 - 第二,
a[i - 1][j - 1]
的值,意义是,S[0 ~ j-1]
中等于T[0 ~ i-1]
的子串个数,哎,也就是s[0 ~ j-1]
这个串中, 差一个T[i]
就齐了的字串个数,此时刚好S[j] == T[i]
,把S[j]
放到末尾不就齐了吗?
以上,可以得出 当T[i] == S[j]
时,a[i][j] = a[i][j - 1] + a[i - 1][j - 1]
- 第一,
因此,状态方程知道了。
#init 这里初始化a[0][i]
a[0][0] = (S[0] == T[0]) ? 1 : 0;
for i in range(1, ls):
a[0][i] = a[0][i-1] + 1 if T[0] == S[i]
a[0][i] = a[0][i-1] if T[0] != S[i]
for i:
for j:
if S[j] != T[i]:
a[i][j] = a[i][j - 1]
elif S[j] == T[i]:
a[i][j] = a[i - 1][j - 1] + a[i - 1][j]
具体Python代码如下
class Solution:
def numDistinct(self, s, t):
if s == '' or t == '':
return 0
LS = len(s)
LT = len(t)
a = [[0 for _ in range(LS)] for _ in range(LT)]
if s[0] == t[0]:
a[0][0] = 1
for i in range(1, LS):
if s[i] == t[0]:
a[0][i] = a[0][i - 1] + 1
else:
a[0][i] = a[0][i - 1]
for i in range(1, LT):
for j in range(1, LS):
if s[j] == t[i]:
a[i][j] = a[i - 1][j - 1] + a[i][j - 1]
else:
a[i][j] = a[i][j-1]
return a[LT-1][LS-1]