链接
https://leetcode-cn.com/problems/distinct-subsequences/
耗时
解题:36 min
题解:53 min
题意
给定一个字符串 s 和一个字符串 t ,计算在 s 的子序列中 t 出现的个数。
字符串的一个 子序列 是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字符串。(例如,“ACE” 是 “ABCDE” 的一个子序列,而 “AEC” 不是)
题目数据保证答案符合 32 位带符号整数范围。
提示:
- 0 <= s.length, t.length <= 1000
- s 和 t 由英文字母组成
思路
dp[i][j] 表示在 s[0:j-1] 的子序列中 t[0:i-1] 出现的个数。以第一个样例为例:
r a b b b i t
1 1 1 1 1 1 1 1
r 0 1 1 1 1 1 1 1
a 0 0 1 1 1 1 1 1
b 0 0 0 1 2 3 3 3
b 0 0 0 0 1 3 3 3
i 0 0 0 0 0 0 3 3
t 0 0 0 0 0 0 0 3
观察可以发现存在如下递推关系式:
d p [ i ] [ j ] = { d p [ i ] [ j − 1 ] + d p [ i − 1 ] [ j − 1 ] ( t [ i − 1 ] = = s [ j − 1 ] ) d p [ i ] [ j − 1 ] ( o t h e r s ) dp[i][j] = \begin{cases} dp[i][j-1] + dp[i-1][j-1] \ \ \ \ \ (t[i-1]==s[j-1]) \\ dp[i][j-1] \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ (others) \end{cases} dp[i][j]={dp[i][j−1]+dp[i−1][j−1] (t[i−1]==s[j−1])dp[i][j−1] (others)
解读:dp[i][j] 可以由两个状态得到,一是当 t[i-1]==s[j-1] 时,s[0:j-2] 的子序列中 t[0:i-2] 出现的个数 dp[i-1][j-1],s[0:j-1] 的子序列中 t[0:i-1] 出现的个数必然包含这些数量 dp[i-1][j-1];二是 s[0:j-2] 的子序列中 t[0:i-1] 出现的个数 dp[i][j-1],无论 t[i-1] 和 s[j-1] 相等与否,s[0:j-1] 的子序列中 t[0:i-1] 出现的个数必然包含这些数量 dp[i][j-1]。
细节:
- 为了第一行第一列能使用这个递推式,所以将第一行和第一列都空出来,然后为了第二行的正确使用,将第一行的所有元素赋值为 1。
- 虽然结果不会超过 int,但是中间值可能会超过 int,所以要使用 long long。
时间复杂度: O ( m n ) O(mn) O(mn)
AC代码
class Solution {
public:
int numDistinct(string s, string t) {
int n = s.size();
int m = t.size();
if(n <= m) {
return (s == t)?1:0;
}
vector<vector<long long>> dp(m+1, vector<long long>(n+1, 0));
for(int j = 0; j <= n; ++j) dp[0][j] = 1;
for(int i = 1; i <= m; ++i) {
for(int j = 1; j <= n; ++j) {
dp[i][j] = dp[i][j-1] + ((t[i-1]==s[j-1])?dp[i-1][j-1]:0);
}
}
return dp[m][n];
}
};