Offer 驾到,掘友接招!我正在参与2022春招系列活动-刷题打卡任务,点击查看活动详情。
前言
力扣第115题 不同的子序列 如下所示:
给定一个字符串 s ****和一个字符串 t ,计算在 s 的子序列中 t 出现的个数。
字符串的一个 子序列 是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字符串。(例如,"ACE" 是 "ABCDE" 的一个子序列,而 "AEC" 不是)
示例 1:
输入: s = "rabbbit", t = "rabbit"
输出 : 3
解释:
如下图所示, 有 3 种可以从 s 中得到 "rabbit" 的方案。
rabbbit
rabbbit
rabbbit
一、思路
我们首先要直到什么是 子序列?
子序列 是指在字符串中删除(也可不删除)但不交换字符串相对位置后组成的新字符串。
举个例子,ab 的子序列就有如下四种可能:
abab- `` (空字符串)
所以这一题我们翻译一下就是:寻找到 s 的子序列中出现字符串 t 的个数
按照上面的意思岂不是可以使用递归来生成字符串 s 的所有子序列,然后在子序列中找到 t 出现的次数?然而使用递归的话会因为时间复杂度过高而超时。
所以在碰到字符串相关的题目不妨使用 动态规划 来解决。
动态规划 的步骤大致如下所示:
- 定义二维数组
我们假设 dp[i][j] 为:** s从下标 i 到末尾的子字符串的子序列中中 t 从下标 j 到末尾字符串出现的个数**
- 状态转移方程
先处理边界情况
- 当
j == n,此时t[j:]为空字符串,空字符串是任何字符串的子序列,故dp[i][n] = 1,即二维数组最后一列为1 - 当
i == m,且 j < n,此时s[i:]为空字符串,而t[j:]不为空字符串,故dp[m][j] = 0 - 当
i < m 且 j < n时:- 如
s[i] == t[j],那么就考虑s[i]这个字符串是否参与匹配?如匹配则dp[i][j] = dp[i+1][j+1]。如不参与匹配,则dp[i][j] = dp[i+1][j]综上dp[i][j] = dp[i+1][j+1] + dp[i+1][j] - 如
s[i] != t[j],只能将s的子字符串缩小,故dp[i][j] = dp[i+1][j]
- 如
二、实现
实现代码
我们在填写二维表格时是从后向前填写的。因为子字符串的长度是由短到长的一个过程。
public int numDistinct(String s, String t) {
int m = s.length(), n = t.length();
if (m < n) {
return 0;
}
int[][] dp = new int[m + 1][n + 1];
// 初始化最后一列
for (int i = 0; i <= m; i++) {
dp[i][n] = 1;
}
for (int i = m - 1; i >= 0; i--) {
char sChar = s.charAt(i);
for (int j = n - 1; j >= 0; j--) {
char tChar = t.charAt(j);
if (sChar == tChar) {
// s[i] 两种情况:参加匹配或不参加
dp[i][j] = dp[i + 1][j + 1] + dp[i + 1][j];
} else {
// 将 s 子字符串长度缩小
dp[i][j] = dp[i + 1][j];
}
}
}
return dp[0][0];
}
测试代码
public static void main(String[] args) {
int ret = new Number115().numDistinct("rabbbit", "rabbit");
System.out.println(ret);
}
结果
三、总结
感谢看到最后,非常荣幸能够帮助到你~♥
如果你觉得我写的还不错的话,不妨给我点个赞吧!如有疑问,也可评论区见~