问题描述:
给定一个字符串s 和一个字符串 t,计算在s的子序列中 t 出现的个数。
字符串的一个子序列是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置 所组成的新字符串。(例如,"ACE"是"ABCDE"的一个子序列,而"AEC"不是)。
题目数据保证答案符合32位带符号整数范围。
0<=s . length, t. length<=1000, s和t由英文字母组成。
动态规划解决:
题目中说“计算在s的子序列中 t 出现的个数。”,翻译就是“在所有s 的子序列中 字符串 t 出现的次数”。
首先,我们定义状态变量,设dp[i][j]表示 字符串t 的前 i 个字符在 s 的前 j 个字符的子序列中出现的次数(也可以说是字符 串s 的前j个字符组成的子序列中,和字符串t 的前i个字符组成的字符串一样的有多少 个)。
那么最终我们只需要求出dp[tlen][ slen]即可(其中tlen和slen分 别表示字符串t和s的长度)。
然后,列出递推方程:
1,如果t 的第 i 个字符等于 s 的第 j 个字符,即t.at(i)==s.at(j) :
便有两种情况:
a,若使字符串 s 的第j 个字符 与 字符串 t 的 第i 个字符 匹配,那我们就只需要在 s 的 前 j-1个字符的子序列中找 t 的前 i -1个字符出现的次数 。
b,若使两者不匹配,那么我们就需要在 s 的前 j-1 个字符的子序列中找 t 的前i 个字符 出现的次数。如下图:
2,如果 t.at(i) ! = s.at(j) ,则只需要在 s 的前 j-1 个字符中 找 t 前i 个字符出现的次数,即 dp[i][j]=dp[i][j-1].
代码:
for (int j = 1; j <= sLength; j++)
{
if (t.charAt(i - 1) == s.charAt(j - 1)) {
//如果字符串t的第i个字符和s的第j个字符一样,
//那么有两种选择,并且两种选择同时发生,所以要相加。
dp[i][j] = dp[i - 1][j - 1] + dp[i][j - 1];
}
else
{
//如果字符串t的第i个字符和s的第j个字符不一样,
//我们只能用字符串s的前j-1个字符来计算他包含的数量
dp[i][j] = dp[i][j - 1];
}
最后,找出边界条件。因为空字符串“ ” 是任何字符串的子序列 ,所以 边界条件是:
dp[0][j] =0
最终代码:
class Solution {
public:
int numDistinct(string s, string t)
{
int slen=s.size();
int tlen=t.size();
if(slen<tlen)
{
return 0;
}
vector<vector<unsigned long>> dp(tlen+1,vector<unsigned long>(slen+1,0));
for(int j=0;j<=slen;j++)
{
dp[0][j]=1;
}
for(int i=1;i<=tlen;i++)
{
for(int j=1;j<=slen;j++)
{
if(t.at(i-1)==s.at(j-1))
{
dp[i][j]=dp[i-1][j-1]+dp[i][j-1];
}
else
{
dp[i][j]=dp[i][j-1];
}
}
}
return dp[tlen][slen];
}
};