题目描述:
给定一个字符串 S 和一个字符串 T,计算在 S 的子序列中 T 出现的个数。
一个字符串的一个子序列是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字符串。(例如,“ACE” 是 “ABCDE” 的一个子序列,而 “AEC” 不是)
题目数据保证答案符合 32 位带符号整数范围。
示例:
输入:S = “babgbag”, T = “bag”
输出:5
解释:
如下图所示, 有 5 种可以从 S 中得到 “bag” 的方案。
(上箭头符号 ^ 表示选取的字母)
babgbag
^^ ^
babgbag
^^ ^
babgbag
^ ^^
babgbag
^ ^^
babgbag
^^^
思路:
1.确定状态:这道题输出的结果,依赖于字符串s和t,所以需要准备一个二维数组res[][];
我们通过观察示例可以发现,当拿t字符串中的前一个、前两个和前三个字符与s比较,结果是有规律的。这也是我们自己手动比较时的大概步骤,先找到s字串中和t[0]相同的位置,然后再对其他位进行比较。
所以可以设res[i][j],代表s字串的前j个字符中,包含t字串前i个字符的子串数量;
2.找到状态转移方式:
当i不变,j增加时,如果t[i] == s[j+1],则之前所有符合条件的结果,都可以再生效一次;
比如s字串前j个字串为“babag”,t字串的前i个字串为“bag”,则此时res[i][j] = 3;如果s[j+1] = t[i] = ‘g’,那么s字串前面的j个字符满足的情况会继续满足;之前满足t[i-1]的情况,也会在此生效;
所以res[i][j] = res[i-1][j-1] + res[i][j-1]; 其实把这个二维数组画出来大概就都懂了。
3.边界情况:
s为空时输出0,t为空时输出1,因为空集也是子集。然后从小到大求出res[t.size-1][s.size-1];
class Solution {
public:
int numDistinct(string s, string t) {
int m = s.size();
int n = t.size();
if( m == 0 )
return 0;
if(n == 0)
return 1;
vector<vector<long long int>> a(n, vector<long long int>(m, 0));
if(s[0] == t[0])
a[0][0] = 1;
for(int i = 1; i<m; ++i){
if(s[i] == t[0])
a[0][i] = a[0][i-1]+1;
else
a[0][i] = a[0][i-1];
}
for(int i = 1; i<n; ++i){
for(int j = i; j<m; ++j){
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[n-1][m-1];
}
};