题目描述
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.
算法分析
我们先对例子分析一下,T能在S中能找到多对不同的子序列,换个角度来想,实际上对于某些重复的序列能进行几次组合。而在例子中 rabbit中的‘bb’要在rabbbit中‘bbb’找出组合数,按照这个思路可以算出C(3,2) = 3 也就是这道题的答案。这种想法其实具有很大局限性,比如在’ABBBAB’中就不能用这个方法找到’AB’不同子序列的个数了。
不过这种分析给我们提供了一个思路,也就是要计算重复出现的字母,用动态规划的思维来想,第一想到的应该是构建一个数组来储存答案,然后通过状态转移方程一直迭代到最后达到答案。
这里我们对简单例子可以手写构建一个数组
A B B B A B 1 1 1 1 1 1 1 A 0 1 1 1 1 2 2 B 0 0 1 2 3 3 5
答案就是 5
这里解释一下这个数组为什么是这样的:
(首先我们定义数组 ans[t.length()+1][s.length()+1])
对于 第 0行来说,意义是对于T为空集的时候,总有唯一一个解
对于 第 0 列来说,对于S为空集的时候,当T不为空集的时候总是没有解
然后对于其他我们有状态转移方程
ans[i][j] = ans[i][j-1] + (T[i-1]==S[j-1]? ans[i-1][j-1] : 0)
分析一下这个状态方程,对于ans[i][j-1]项应该很容易理解,就是每次加入多S的一个字符,目前的序列数不比不加入的少,这也就是动态规划里面的基本的一项。而当T[i-1]==S[j-1]时,证明该字符重复出现或者第一次出现,那么应该累加上T还没加上该字符时候的序列数。
代码如下:
int numDistinct(string s, string t){
vector<vector<int> > ans(t.length()+1,vector<int>(s.length()+1));
for(int i = 0 ;i < s.length()+1; i++)
ans[0][i] = 1;
for(int i = 1 ;i < t.length()+1; i++){
bool first = false;
for(int j = i ; j < s.length()+1; j++ ){
ans[i][j] = ans[i][j-1] + (t[i-1]==s[j-1]?ans[i-1][j-1]:0);
}
}
return ans[t.length()][s.length()];
}