-
Notifications
You must be signed in to change notification settings - Fork 13
/
Copy pathSolution0115.java
67 lines (63 loc) · 2.92 KB
/
Solution0115.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
// 115. 不同的子序列
/*
动态规划:
1、题目简化:求s的子序列中t出现的个数
1、定义dp数组:dp[i][j] 表示 s前i个字符 中出现 t前j个字符 的个数
2、初始化:
1)二维dp数组扩容:增加空字符串这一最小规模的情况,方便直观初始化
2)dp[i][0] = 1 表示 s前i个字符 中出现 t前0个字符 的个数为1,即空字符串是任何字符串的子集
dp[0][j] = 0 表示 s前0个字符 中出现 t前j个字符 的个数为0,即空字符串不会出现任何字符串
dp[0][0] = 1 表示 s前i个字符 中出现 t前0个字符 的个数为1,即空字符串是空字符串的子集
3、状态转移方程:
1)s中不同索引位置的字符组合,代表不同的子序列
2)如果s[i] == s[j],那么s有两种方式可以匹配t
第一种是用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] = dp[i - 1][j - 1] + dp[i - 1][j];
3)如果s[i] != s[j],那么s只能不用s[i]匹配t[j],那么出现个数为 s前i-1个字符中出现t前j个字符的个数,即 dp[i - 1][j]
所以 dp[i][j] = dp[i - 1][j];
4、遍历dp数组填表:
1)两个for循环遍历二维dp数组每个位置,根据状态转移方程计算该位置的出现个数
2)遍历顺序决定了哪些位置是计算过的、是已知状态,外层遍历字符串s,内层遍历字符串t。
从二维数组角度看,遍历顺序是从上到下,从左到右,所以遍历到(i,j)位置时,其左方、上方、左上方状态都已经遍历计算过了。
从两个字符串角度看,两个指针分别指向两个字符串,两者遍历顺序都是从左到右,所以遍历到(i,j)位置时,其左边其他位置都遍历计算过了。
所以求 dp[i][j] 时,dp[i - 1][j - 1]、dp[i - 1][j] 都是已知状态了
5、返回结果:最后一个状态就是结果
s = "babgbag", t = "bag"
二维数组:
'' b a g
'' 1 0 0 0
b 1 1 0 0
a 1 1 1 0
b 1 2 1 0
g 1 3 1 1
b 1 3 4 1
a 1 3 4 1
g 1 3 4 5
字符串:
s: b a b g b a g
↑
i
t: b a g
↑
j
*/
class Solution {
public int numDistinct(String s, String t) {
int n = s.length(), m = t.length();
int[][] dp = new int[n + 1][m + 1];
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];
}
}
}
return dp[n][m];
}
}