给定一个字符串 S 和一个字符串 T,计算在 S 的子序列中 T 出现的个数。
一个字符串的一个子序列是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字符串。(例如,"ACE"
是 "ABCDE"
的一个子序列,而 "AEC"
不是)
示例 1:
输入: S = "rabbbit", T = "rabbit"
输出: 3
解释:
如下图所示, 有 3 种可以从 S 中得到 "rabbit" 的方案。
(上箭头符号 ^ 表示选取的字母)
rabbbit
^^^^ ^^
rabbbit
^^ ^^^^
rabbbit
^^^ ^^^
示例 2:
输入: S = "babgbag", T = "bag"
输出: 5
解释:
如下图所示, 有 5 种可以从 S 中得到 "bag" 的方案。
(上箭头符号 ^ 表示选取的字母)
babgbag
^^ ^
babgbag
^^ ^
babgbag
^ ^^
babgbag
^ ^^
babgbag
^^^
解题思路
我们先进行一些准备工作。
s_len, t_len, result = len(s), len(t), 0
if s_len < t_len:
return result
if s_len == t_len:
if s == t:
return 1
return result
首先你一定可以想到这样的暴力解法。从S
中选出len(T)
大小的全部组合,然后判断这些组合中有多少个和T
是一样的。
for i in combinations(s, t_len):
new_t = "".join(i)
if new_t == t:
result += 1
这种匹配问题,我们很容易想到通过动态规划来做。但是如果我们直接思考递归过程,很难想出具体的递推公式。对于两个字符串的比较,在我们没有其他办法的时候,我们不妨先建立一个二维的数组,观察一下两个字符串的匹配情况
Ø r a b b b i t
Ø 1 1 1 1 1 1 1 1
r 0 1 1 1 1 1 1 1
a 0 0 1 1 1 1 1 1
b 0 0 0 1 2 3 3 3
b 0 0 0 0 1 3 3 3
i 0 0 0 0 0 0 3 3
t 0 0 0 0 0 0 0 3
首先说明一下上面这个矩阵的含义,横向表示S
,纵向表示T
,每个元素表示当前横坐标向左的S
字符串最多可以匹配几次当前纵坐标向上的T
字符串。举个例子,对于坐标3,4
,此时S=rabb
,而T=rab
,所以可以匹配两次。观察上面这个矩阵,我们可以的到这样的规律,假设我们此时的坐标是(i,j)
,如果S[j]==T[i]
,我们的矩阵mem[i+1][j+1]=mem[i][j]+mem[i+1][j]
,否则的话mem[i+1][j+1]=mem[i+1][j]
。
class Solution:
def numDistinct(self, s, t):
"""
:type s: str
:type t: str
:rtype: int
"""
s_len, t_len = len(s), len(t)
mem = [[0]*(s_len+1) for _ in range(t_len+1)]
for i in range(s_len+1):
mem[0][i] = 1
for i in range(t_len):
for j in range(s_len):
if s[j] == t[i]:
mem[i+1][j+1] = mem[i][j] + mem[i+1][j]
else:
mem[i+1][j+1] = mem[i+1][j]
return mem[-1][-1]
如果我们从另外一个角度去看这个问题,我们按照递归的思路去考虑,每次遍历s
的一个元素,然后思考t
的前缀在s[:i]
中出现的次数,我们会得到这样的矩阵
r a b b i t
0 0 0 0 0 0 Ø
1 0 0 0 0 0 r
1 1 0 0 0 0 a
1 1 1 0 0 0 b
1 1 2 1 0 0 b
1 1 3 3 0 0 b
1 1 3 3 3 0 i
1 1 3 3 3 3 t
我们不难发现这样的规律,我们每次计算新的一行当中的元素,假设此时的坐标是i,j
,如果s[i]==t[j]
,我们此时的第j+1
列的值,会等于原来j+1
列的值再加上原来j
列的值,也就是↘和↓相加。
class Solution:
def numDistinct(self, s, t):
"""
:type s: str
:type t: str
:rtype: int
"""
t_len = len(t)
mem = [1]+[0]*t_len
for s_c in s:
for i in range(t_len-1, -1, -1):
if t[i] == s_c:
mem[i+1] += mem[i]
return mem[-1]
是不是很酷!!!
我将该问题的其他语言版本添加到了我的GitHub Leetcode
如有问题,希望大家指出!!!