力扣第115题 不同的子序列 c++ 动态规划 注释版 + Java代码
题目
困难
相关标签
给你两个字符串 s 和 t ,统计并返回在 s 的 子序列 中 t 出现的个数,结果需要对 109 + 7 取模。
示例 1:
输入:s = "rabbbit", t = "rabbit"输出:3解释: 如下所示, 有 3 种可以从 s 中得到 "rabbit" 的方案。rabbbitrabbbitrabbbit
示例 2:
输入:s = "babgbag", t = "bag"输出:5解释: 如下所示, 有 5 种可以从 s 中得到 "bag" 的方案。babgbagbabgbagbabgbagbabgbagbabgbag
提示:
1 <= s.length, t.length <= 1000s和t由英文字母组成
思路和解题方法
创建dp数组:在这段代码中,我们使用了一个二维的dp数组来保存子问题的解。数组的大小是(s.size() + 1)×(t.size() + 1),其中s.size()表示字符串s的长度,t.size()表示字符串t的长度。所以dp数组的行数是s的长度加1,列数是t的长度加1。
初始化边界条件:在动态规划的问题中,通常需要初始化一些特殊情况下的解。在这段代码中,我们初始化了dp数组的第一行和第一列。根据题目要求,当t为空串时,s中只有一个空串与之匹配,所以dp[i][0]的初始值都是1。另外,当s为空串时,任何非空的t都无法与之匹配,所以dp[0][j]的初始值都是0,其中j从1到t的长度。
填充dp数组:通过两层循环遍历dp数组,我们可以逐步计算出dp[i][j]的值。循环中的i表示s的索引,j表示t的索引。在每次迭代中,我们比较s[i-1]和t[j-1]的字符:
如果s[i-1]等于t[j-1],说明当前字符能够匹配,此时有两种选择:
- 使用s[i-1]匹配t[j-1]:即dp[i][j] = dp[i-1][j-1],表示之前的方案数加上这种选择。
- 不使用s[i-1]匹配t[j-1]:即dp[i][j] += dp[i-1][j],表示继承之前的方案数。
如果s[i-1]不等于t[j-1],说明当前字符不能够匹配,此时只能不使用s[i-1],故有dp[i][j] = dp[i-1][j]。
返回结果:最后,我们返回dp[s.size()][t.size()],即s中有多少个子序列与t相同的方案数。
复杂度
时间复杂度:
O(n*m)
时间复杂度:
- 遍历两个字符串,共需执行 s.size() * t.size() 次循环。
- 在每个循环中,进行常数时间(O(1))的比较和赋值操作。 所以,总体时间复杂度为 O(s.size() * t.size())。
空间复杂度
O(n*m)
空间复杂度:
使用了一个二维数组 dp,大小为 (s.size() + 1) * (t.size() + 1),空间复杂度为 O(s.size() * t.size())。
c++ 代码
int numDistinct(string s, string t) {
// 创建一个二维数组 dp,用于存储状态转移值
vector<vector<unsigned long long>> dp(s.size() + 1, vector<unsigned long long>(t.size() + 1));
// 初始化边界条件
for (int i = 0; i < s.size(); i++) {
dp[i][0] = 1;
}
for (int j = 1; j < t.size(); j++) {
dp[0][j] = 0;
}
// 计算状态转移值
for (int i = 1; i <= s.size(); i++) {
for (int j = 1; j <= t.size(); j++) {
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];
}
}
}
// 返回最后一个状态转移值,即字符串 t 在字符串 s 中出现的不同子序列的个数
return dp[s.size()][t.size()];
}
Java代码
class Solution {
public int numDistinct(String s, String t) {
// 创建一个二维数组dp,大小为(s.length() + 1) * (t.length() + 1)
int[][] dp = new int[s.length() + 1][t.length() + 1];
// 初始化边界条件,当t为空字符串时,s中的任意子串都可以匹配,所以dp[i][0] = 1
for (int i = 0; i < s.length() + 1; i++) {
dp[i][0] = 1;
}
// 动态规划计算dp数组的值
for (int i = 1; i < s.length() + 1; i++) {
for (int j = 1; j < t.length() + 1; j++) {
// 如果当前s的字符与t的字符相等,说明可以选择匹配或者不匹配
if (s.charAt(i - 1) == t.charAt(j - 1)) {
// 若选择匹配,则需要找到前面子串的匹配数,即dp[i - 1][j - 1]
// 若选择不匹配,则需要找到前面子串的匹配数,即dp[i - 1][j]
dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j];
} else {
// 如果当前s的字符与t的字符不相等,则无法匹配,所以匹配数与前面子串相同,即dp[i - 1][j]
dp[i][j] = dp[i - 1][j];
}
}
}
// 返回dp数组右下角的值,即整个s和t的匹配数
return dp[s.length()][t.length()];
}
}
觉得有用的话可以点点赞,支持一下。
如果愿意的话关注一下。会对你有更多的帮助。
每天都会不定时更新哦 >人< 。
浙公网安备 33010602011771号