class Solution {
public int numDistinct(String s, String t) {
int m = s.length(), n = t.length();
if (m < n) {
return 0;
}
int[][] dp = new int[n + 1][m + 1];
for (int i = 0; i <= m; i++) {
dp[0][i] = 1;
}
for (int i = 1; i <=n; i++) {
char tChar = t.charAt(i-1);
for (int j = 1; j <=m; j++) {
char sChar = s.charAt(j-1);
if (sChar == tChar) {
dp[i][j] = dp[i - 1][j - 1] + dp[i][j-1];
} else {
dp[i][j] = dp[i][j-1];
}
}
}
return dp[n][m];
}
}
核心点:转移方程
if (sChar == tChar) {
dp[i][j] = dp[i - 1][j - 1] + dp[i][j-1];
} else {
dp[i][j] = dp[i][j-1];
dp[i][j]表示s的前j部分的子序列在t的前i部分出现的次数。
对于dp[i][j]情况时,如果s和t的结尾相同即sChar == tChar,要想计算出t[i]出现次数,考虑两种情况。
其一: s[j-1]中有没有t[j-1],这个t[i-1]是我们当前要找的t[i]的前缀,
因为sChar==tChar所以t[i-1]+ sChar就是t[i]了。这对应着dp[i - 1][j - 1]
其二: 我们在第一种情况中,主要寻找前缀,因为前缀加上s[j]的结尾就是我们要找的t[i]。
在第二种情况我们就寻找s[j-1]中有没有t[i](不需要考虑最后一个字母是否相等)。
这种情况对应dp[i][j-1]
因此 当sChar == tChar,将两种情况的个数相加就行,
也就是能和最后一个新添加的字母组成t[i]的前缀个数+在s[j-1]中以及出现过的t[i]的个数。
总的状态转移方程为: dp[i][j] = dp[i - 1][j - 1] + dp[i][j-1];
而当sChar != tChar时,也就是 结尾新添加的字母不同时,s[j-1]的前缀即便加上结尾字母也不等于t[i],我们就是能寻找s[j-1]中以及出现的t[i],他就对应上面的情况二:dp[i][j-1]
因此这时的状态转移方程为: dp[i][j-1]
边界条件怎么生成可以从代码中看出。
如果是 t[0] ,也就是空串,当然有且只有一个子序列也就是空序列了。
而如果是 s[0],它本身是空串,而 i > 0 ,无论如何也找不出子序列了,因为 s 长度都已经 t 了。