题目:
https://leetcode.com/problems/distinct-subsequences/
Given a string S and a string T, count the number of distinct subsequences of T in S.
A subsequence of a string is a new string which is formed from the original string by deleting some (can be none) of the characters without disturbing the relative positions of the remaining characters. (ie, “ACE” is a subsequence of “ABCDE” while “AEC” is not).
Here is an example:
S = “rabbbit”, T = “rabbit”Return 3.
分析:
这个题意是,给两个字符串 S 和 T,S 中有多少个子集和 T 一样,也就是可以删除 S 的 0 或者多个字母,来得到和 T 一样的字符串,有多少种方式。
Input & Output:
输入就是两个字符串,S 和 T。
输出是数字,即方式的个数。
Edge & Test Cases:
如果 S为空,T为空,则返回1。
如果 S不为空,T为空,则返回1。
如果 S为空,T不为空,则返回0。
如果 S 和 T 都不为空,则返回方式的个数。
Brainstorming:
最初映入脑海的想法是,从字母的个数上去判断,然后从组合的角度去计算,但是行不通,因为这里涉及到顺序,统计完每个字母的个数后,并不能保证它的顺序。
另外一种方法是递归,参考此文。
这个问题和字符串匹配类的问题相似,乍一看来会觉得无从下手,因为删除可以有很多种方式,比如例子,可以在中间删除任意一个b,但是你要如何很快很省力地知道有多少个b呢,又要如何判断剩余字母呢。
如果冷静下来,试着把这个问题分解一下,想一想有什么内在的联系可以缩小问题的范围。
如果 S=rab,T=ra,那么方式有1种,因为末位字母b不等于a,所以可以看作把b删掉,那结果就等于去掉后的方式有多少种。
如果 S=raa,T=ra,即末位字母a等于a,那么一种可能是,仍然把S的末位a去掉,计算S剩余部分和T的匹配种数,另一种可能是,让S的末位a与T的末位a先匹配,所以可以视为都划掉,然后计算S和T的剩余部分的匹配方式数目。
由这种思路,我们可以采用DP来解决这个问题,因为有两个字符串,所以构造一个二维数组。根据上面分析,数组的每个元素,即递推量dp[i][j]就是T的前i个字符构成的串,在S的前j个字符构成的串中,匹配方式有多少个。
如下图:
这是题里给定例子的dp矩阵,最后返回右下角的数字。
python 代码:
class Solution(object):
def numDistinct(self, s, t):
dp = [[0 for j in range(len(s)+1)] for i in range(len(t)+1) ]
for j in range(len(s)+1):
dp[0][j] = 1
for i in range(len(t)):
for j in range(len(s)):
if t[i]==s[j]:
dp[i+1][j+1] = dp[i+1][j] + dp[i][j]
else:
dp[i+1][j+1] = dp[i+1][j]
return dp[len(t)][len(s)]
Debugging:
将 Edge & Test Cases 部分提到的几种情况,代入进去正确。
注意,dp的长宽分别加了1,因为考虑了空的情况。同时,循环角标 i,j 指的是相应位置 S 和 T 的元素,留意和dp矩阵相应位置的关系,不要超出范围。