问题描述
来源:LeetCode第115题
难度:困难
给定一个字符串s和一个字符串t,计算在s的子序列中t出现的个数。
字符串的一个子序列是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置
所组成的新字符串。(例如,"ACE"是"ABCDE"的一个子序列,而"AEC"不是)
题目数据保证答案符合32位带符号整数范围。
示例 1:
输入:s = “ra b b b i t”, t = “ra b b i t”
输出:3
解释:
如下图所示, 有3种可以从s中得到"ra b b i t"的方案。
ra b bbi t
ra bbb i t
rabb b i t
示例 2:
输入:s = " b a b g b a g “, t = " b a g "
输出:5
解释:
如下图所示, 有5种可以从s中得到” b a g "的方案。
b abgb a g
b ab g b ag
ba b g ba g
b abg ba g
b a b gb a g
提示:
0<=s . length, t. length<=1000
s和t由英文字母组成
动态规划解决
这题说的是s的子序列中出现t的个数,翻译一下就是字符串s的所有子序列中,和字符
串t完全一样的有多少个。
我们定义dp[i][ j]表示t的前i个字符可以由s的前j个字符组成的个数(也可以说是字符
串s 的前j个字符组成的子序列中,和字符串t 的前i个字符组成的字符串一样的有多少
个)。
那么最终我们只需要求出dp[tLength][ sLength]即可(其中tLength和sLength分
别表示字符串t和s的长度)。
如果字符串t的第i个字符和字符串s的第j个字符一样,如下所示

如上图所示我们可以有两种选择。
如果字符串t的第i个字符和字符串s的第j个字符不一样,也就是说字符串s的第j个字符
不能匹配字符串t的第i个字符。那么我们只能计算字符串s的前j -1个字符构成的子序列
中包含字符串t的前i个字符组成的字符串的个数。
所以递推公式如下
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];
}
动态规划的三个步骤就是定义状态,列出递推公式,找出边界条件。前面两步我们都完
成了,我们来看最后一个。因为空字符串" "是所有字符串的子集,所以当字符串t为空
的时候,dp[0][ j]=1;
我们来看下最终代码
public int numDistinct(String s, String t) {
//sLength和tLength分别是两个字符串的长度
int sLength = s.length();
int tLength = t.length();
int[][] dp = new int[tLength + 1][sLength + 1];
//base case 边界条件
for (int j = 0; j <= sLength; j++) {
dp[0][j] = 1;
}
for (int i = 1; i <= tLength; i++) {
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];
}
}
}
return dp[tLength][sLength];
}
本文介绍了如何使用动态规划解决LeetCode第115题,计算给定字符串s的所有子序列中与字符串t完全相同的子串数量。通过定义dp数组并列出递推公式,解决了字符串t的前i个字符在s的子序列中的匹配计数问题。

被折叠的 条评论
为什么被折叠?



