欢迎访问LeetCode题解合集
题目描述
给定一个字符串 s
和一个字符串 t
,计算在 s
的子序列中 t
出现的个数。
字符串的一个 子序列 是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字符串。(例如,"ACE"
是 "ABCDE"
的一个子序列,而 "AEC"
不是)
题目数据保证答案符合 32 位带符号整数范围。
示例 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
^^^
提示:
- $0 \le s.length, t.length \le 1000$
s
和t
由英文字母组成
题解:
动态规划。
设 f[i][j]
表示 t[1...j]
在 s]1...i]
中出现的次数。状态转移方程如下:
- 如果
s[i] == t[j]
,s[i]
可以与t[j]
匹配,也可以不与t[j]
匹配,所以f[i][j] = f[i - 1][j] + f[i - 1][j - 1]
- 如果
s[i] != t[j]
,此时f[i][j] = f[i - 1][j]
时间复杂度:$O(n*m)$
额外空间复杂度:$O(n*m)$
class Solution {
public:
using LL = long long;
int numDistinct(string s, string t) {
int ls = s.size();
int lt = t.size();
if ( ls < lt ) return 0;
vector<vector<LL>> f( ls + 1, vector<LL>( lt + 1 ) );
for ( int i = 0; i <= ls; ++i ) f[i][0] = 1;
int up = lt;
for ( int i = 1; i <= ls; ++i ) {
if ( lt >= i ) up = i;
else up = lt;
for ( int j = 1; j <= up; ++j ) {
f[i][j] = f[i - 1][j];
if ( s[i - 1] == t[j - 1] )
f[i][j] += f[i - 1][j - 1];
}
}
return f[ls][lt];
}
};
/*
时间:0ms,击败:100.00%
内存:7.1MB,击败:71.58%
*/
可以发现 f[i][j]
只跟 f[i - 1][j]
和 f[i - 1][j - 1]
有关,可使用滚动数组优化:
class Solution {
public:
using LL = long long;
int numDistinct(string s, string t) {
int ls = s.size();
int lt = t.size();
if ( ls < lt ) return 0;
vector<LL> f( lt + 1 );
f[0] = 1;
int up = lt;
LL pre, tmp;
for ( int i = 1; i <= ls; ++i ) {
if ( lt >= i ) up = i;
else up = lt;
pre = f[0];
for ( int j = 1; j <= up; ++j ) {
tmp = f[j];
if ( s[i - 1] == t[j - 1] )
f[j] += pre;
pre = tmp;
}
}
return f[lt];
}
};
/*
时间:0ms,击败:100.00%
内存:6.2MB,击败:98.35%
*/