题目来源
题目描述
class Solution {
public:
int numDistinct(string s, string t) {
}
};
题目解析
分析数据量
0 <= s.length, t.length <= 1000
:行列对应模型,(10^6)s 和 t 由英文字母组成
:maybe数组作为桶
分析题意
在s串上“挑选”字符,去匹配t
串的字符,求挑选的方法数
暴力递归
抓住“选”,s要按照t来挑选,逐个字符考察选或者不选,分别来到什么状态
举例,s 为babgbag
,t 为bag
,末尾字符相同,于是 s 有两种选择:
- 用 s [ s . l e n g t h − 1 ] s[s.length-1] s[s.length−1]去匹配掉 t [ t . l e n g t h − 1 ] t[t.length-1] t[t.length−1],问题规模缩小:继续考察 b a b g b a babgba babgba和 b a ba ba
- 不这么做,但 t [ t . l e n g t h − 1 ] t[t.length-1] t[t.length−1]仍需被匹配,于是在 b a b g b a babgba babgba中继续挑,考察 b a b g b a babgba babgba和 b a g bag bag
是否用它去匹配,是两种不同的挑选方式,各自做下去所产生的方式数,相加,是大问题的解。
现在我们拆解出规模小一点的子问题。完善一下,定义出递归函数:
返回:从开头到s[i]的子串中,出现『从开头到t[j]的子串』的次数。
即,从 前者 选字符,去匹配 后者,的方案数。
看了s[i]==t[j]的情况,那s[i]!=t[j]的情况呢?s[i]不匹配t[j],唯有拿s[i]之前的子串去匹配
现在两种情况下的递归公式都好写了。递归树底部的 base case 呢?
随着递归压栈,子问题规模(子串长度)在变小:
- 小到 t 变成空串,此时 s 为了匹配它,方式只有1种:什么字符也不用挑(或 s 也是空串,什么都不做就匹配了,方式数也是1)
- 小到 s 变成空串,但 t 不是,s 怎么也匹配不了 t,方式数为 0
class Solution {
int process(string &s, string &t, int i, int j){
if(j < 0){
return 1;
}
if(i < 0){
return 0;
}
if(s[i] == t[j]){
return process(s, t, i - 1, j) + process(s, t, i - 1, j - 1);
}
return process(s, t, i - 1, j);
}
public:
int numDistinct(string s, string t) {
int sLen = s.size(), tLen = t.size();
return process(s, t, sLen - 1, tLen - 1);
}
};
思路
标准的样本对应模型。
-
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]:s只拿前i个字符做子序列,有多少个子序列,字面值等于T的前j个字符的前缀串(样本对应模型是只看最后一个字符能不能要,要不要)
- 可能性1)S[…i]的所有子序列中,都不以s[i]结尾,则dp[i][j]肯定包含dp[i-1][j]
- 可能性2)S[…i]的所有子序列中,都必须以s[i]结尾,这要求S[i] == T[j],则dp[i][j]包含dp[i-1][j-1]
- base case:
- j = = 0 j==0 j==0时, d p [ i ] [ 0 ] = 1 dp[i][0] = 1 dp[i][0]=1 (t为空串, i–>[0…slen])
- i = = 0 i==0 i==0时, d p [ 0 ] [ j ] = 0 dp[0][j] = 0 dp[0][j]=0(s为空串,j—>[1…tlen])
- 状态转移:
- 当 s [ i − 1 ] ! = t [ j − 1 ] s[i-1] != t[j-1] s[i−1]!=t[j−1]时,有 d p [ i ] [ j ] = d p [ i − 1 ] [ j ] dp[i][j] = dp[i-1][j] dp[i][j]=dp[i−1][j]
- 当 s [ i − 1 ] = = t [ j − 1 ] s[i-1] == t[j-1] s[i−1]==t[j−1]时,有 d p [ i ] [ j ] = d p [ i − 1 ] [ j − 1 ] + d p [ i − 1 ] [ j ] dp[i][j] = dp[i-1][j-1] + dp[i-1][j] dp[i][j]=dp[i−1][j−1]+dp[i−1][j]
class Solution {
public:
int numDistinct(string s, string t) {
int slen = s.size(), tlen = t.size();
std::vector<std::vector<int>> dp(slen + 1, std::vector<int>(tlen + 1));
for (int i = 0; i <= slen; ++i) {
for (int j = 0; j <= tlen; ++j) {
if(j == 0){
dp[i][j] = 1;
}else if(i == 0){ // s只拿前0个字符做子序列, T前j个字符
dp[i][j] = 0;
}else{
if(s[i - 1] == t[j - 1]){
dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j];
}else{
dp[i][j] = dp[i - 1][j];
}
}
}
}
return dp[slen][tlen];
}
};