给出字符串S和字符串T,计算S的不同的子序列中T出现的个数。
子序列字符串是原始字符串通过删除一些(或零个)产生的一个新的字符串,并且对剩下的字符的相对位置没有影响。(比如,“ACE”是“ABCDE”的子序列字符串,而“AEC”不是)。
样例
给出S = "rabbbit", T = "rabbit"
返回 3
挑战
Do it in O(n2) time and O(n) memory.
O(n2) memory is also acceptable if you do not know how to optimize memory.
解题思路:
看到有关字符串的子序列或者配准类的问题,首先应该考虑的就是用动态规划Dynamic Programming来求解,这个应成为条件反射。而所有DP问题的核心就是找出递推公式,想这道题就是递推一个二维的dp数组,下面我们从题目中给的例子来分析,这个二维dp数组应为:
Ø r a b b b i t
Ø 1 1 1 1 1 1 1 1
r 0 1 1 1 1 1 1 1
a 0 0 1 1 1 1 1 1
b 0 0 0 1 2 3 3 3
b 0 0 0 0 1 3 3 3
i 0 0 0 0 0 0 3 3
t 0 0 0 0 0 0 0 3
首先,若原字符串不为空,而子序列为空,返回1,因为空串也是任意字符串的一个子序列。
我们通过观察上面的二维数组可以发现,当更新到dp[i][j]时,dp[i][j] >= dp[i][j - 1] 总是成立,再进一步观察发现,当 T[i - 1] == S[j - 1] 时,dp[i][j] = dp[i][j - 1] + dp[i - 1][j - 1],若不等, dp[i][j] = dp[i][j - 1]
public class Solution {
/**
* @param S: A string
* @param T: A string
* @return: Count the number of distinct subsequences
*/
public int numDistinct(String S, String T) {
// write your code here
if (S == null || T == null)
return 0;
// dp[i][j]代表 t.substring(0 ,j)在s.substring(0 , i) 出现的次数
int[][] dp = new int[S.length() + 1][T.length() + 1];
//若原字符串不为空,而子序列为空,返回1,因为空串也是任意字符串的一个子序列。
for (int i = 0; i <= S.length(); i++)
dp[i][0] = 1;
for (int i = 1; i <= S.length(); i++) {
for (int j = 1; j <= T.length(); 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[S.length()][T.length()];
}
}