115. 不同的子序列
Desciption
给定一个字符串 s 和一个字符串 t ,计算在 s 的子序列中 t 出现的个数。
字符串的一个 子序列 是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字符串。(例如,“ACE” 是 “ABCDE” 的一个子序列,而 “AEC” 不是)
题目数据保证答案符合 32 位带符号整数范围。
0 <= s.length, t.length <= 1000
s 和 t 由英文字母组成
示例:
输入:s = "babgbag", t = "bag"
输出:5
解释:
如下图所示, 有 5 种可以从 s 中得到 "bag" 的方案。
babgbag
babgbag
babgbag
babgbag
babgbag
Solution
这种计算数量的题目,可以先考虑dp。
dp[i][j]
表示 s 的前 i 个字符和 t 的前 j 个字符,字符串 t 在 s 的出现次数,思考一下怎么转移?额,好像转移的方程式很难写(写不太出来)
可能我们的 dp 数组的状态表示的不太正确,重新思考一下
dp[i][j]
表示 s 的前 i 个字符和 t 的前 j 个字符,t 的前 j 个字符 在 s 的前 i 个字符中的出现次数。思考怎么转移?
如果 s[i] == t[j]
,那么 dp[i][j]
可以由两种情况转移过来:
- s[i] 和 t[j] 进行匹配,则可以由 s 的前 i-1 个字符和 t 的前 j-1 个字符匹配的数量转移过来,即
dp[i-1][j-1]
- s[i] 和 t[j] 不进行匹配,则可以由 s 的前 i-1 个字符和 t 的前 j 个字符匹配的数量转移过来,即
dp[i-1][j]
这里是否可以由 dp[i][j-1]
转移到 dp[i][j]
呢?应该明显不行,看看dp数组的含义就知道
如果 s[i] != t[j]
,那么 dp[i][j]
只能够由 s 的前 i-1 个字符和 t 的前 j 个字符匹配的数量转移过来,即 dp[i-1][j]
边界状态:dp[i][0] = 1
(0 <= i <= n)
Code
public int numDistinct(String s, String t) {
int n = s.length(), m = t.length();
int[][] dp = new int[1010][m + 10];
for (int i = 0; i < 1010; ++i) {
Arrays.fill(dp[i], 0);
}
for (int i = 0; i <= n; ++i) {
dp[i][0] = 1;
}
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
if (s.charAt(i - 1) == t.charAt(j - 1)) {
dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j];
} else {
dp[i][j] = dp[i - 1][j];
}
System.out.println(" i = " + i + " j = " + j + " dp[i][j] = " + dp[i][j]);
}
}
return dp[n][m];
}