问题
给定一个字符串 S 和一个字符串 T,计算在 S 的子序列中 T 出现的个数。
一个字符串的一个子序列是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字符串。(例如,“ACE” 是 “ABCDE” 的一个子序列,而 “AEC” 不是)
示例 1:
输入: S = “rabbbit”, T = “rabbit”
输出: 3
解释:
如下图所示, 有 3 种可以从 S 中得到 “rabbit” 的方案。
(上箭头符号 ^ 表示选取的字母)
rabbbit
^^^^ ^^
rabbbit
^^ ^^^^
rabbbit
^^^ ^^^
示例 2:
输入: S = “babgbag”, T = “bag”
输出: 5
解释:
如下图所示, 有 5 种可以从 S 中得到 “bag” 的方案。
(上箭头符号 ^ 表示选取的字母)
babgbag
^^ ^
babgbag
^^ ^
babgbag
^ ^^
babgbag
^ ^^
babgbag
^^^
分析
总体来说,解动态规划题目的思路
两种解法
回溯法
很直观的想法,循环在字符串s中找与字符串t的第一个字符相匹配的字符;如果s的当前字符匹配,则递归s的子串和t的子串做同样的查找操作。
如,s=“babgbag” t=“bag”
循环在s中找与t中第一个字符,即b相匹配的字符;找到合适位置,即s的第0个字符,则递归:
s=“abgabg”,t=“ag”
递归终止条件:
如果t为“”,说明t已被匹配完成,计数增加,返回;
否则(t不为“”),如果s为“”,说明没有匹配完,不增加计数,返回。
代码见下。
动态规划
还是以s=“babgbag”,t=“bag”为例
借助一个二维数组dp[][];dp(i,j),表示s截止到第j个字符,能够匹配t.substring(0,i+1)的子序列的个数。
则有以下规则:
- 如果t.charAt(i) == s.charAt(i),则:dp[i][j] = dp[i-1][j-1] + dp[i][j-1]
- 如果t.charAt(i) != s.charAt(i),则:dp[i][j] = dp[i][j-1]
解释一下:
如果字符相同,判断s.substring(0,j+1),即为s_sub中有多少匹配t.substring(0,i+1),记为t_sub的子序列
- 1)如果s.substring(0,j-1)与t.substring(0,i)有m个匹配的子序列,也就是s不包含第j个字符的时候,已经与t_sub有m个匹配的子序列了;又有t.charAt(i) == s.charAt(i),所以应该在加上多出这一个字符多出来的匹配结果,即2)(我写不明白了……)
- 2)多出来的匹配结果,(不管为什么吧)就等于 dp[i-1][j-1]。
代码见下。写的很丑。
代码
回溯法
public int numDistinct(String s, String t) {
// int ans = 0;
helper(s, t);
return ans;
}
private void helper(String s, String t){
// 递归结束条件
if(t.equals("")) {
// 更新ans值
ans ++;
return ;
}else if(s.equals("")){
return ;
}
// 在s中找与t的第一个字符相匹配的字符;
// 找到后,递归在s的子串找和t的子串匹配的字符
char ch = t.charAt(0);
for(int i = 0; i < s.length(); i++){
if(s.charAt(i) == ch){
helper(s.substring(i+1,s.length()), t.substring(1, t.length()));
}
}
return ;
}
动态规划
public int numDistinct(String s, String t) {
//
if(s==null || t==null || s.equals("") || t.equals("")) return 0;
int[][] dp = new int[t.length()][s.length()];
for(int i = 0; i < t.length(); i++){
for(int j = 0; j < s.length(); j++){
if(i == 0){
//第一行单独处理
if(j == 0) {
if(s.charAt(j) == t.charAt(i)) dp[i][j] = 1;
}else {
if(s.charAt(j) == t.charAt(i)){
dp[i][j] = dp[i][j-1] + 1;
}else{
dp[i][j] = dp[i][j-1] ;
}
}
continue;
}
if(j < i){
dp[i][j] = 0;
}else{
// 比较t.charAt(i)与s.charAt(j)是否相等
if(t.charAt(i) == s.charAt(j)){
dp[i][j] = dp[i-1][j-1] + dp[i][j-1];
}else {
dp[i][j] = dp[i][j-1];
}
}
}
}
return dp[t.length()-1][s.length()-1];
}