LeetCode——不同的子序列
Q:给定一个字符串S和一个字符串T,计算S中的T的不同子序列的个数。
字符串的子序列是由原来的字符串删除一些字符(也可以不删除)在不改变相对位置的情况下的剩余字符(例如,"ACE"is a subsequence of"ABCDE"但是"AEC"不是)
例如:
S ="rabbbit", T ="rabbit"
返回3
A:(感谢@WNDKSJDM)
我们需要一个二维数组dp(i)(j)来记录长度为i的字串在长度为j的母串中出现的次数,这里长度都是从头算起的,而且遍历时,保持子串长度相同,先递增母串长度,母串最长时再增加一点子串长度重头开始计算母串。
首先我们先要初始化矩阵,当子串长度为0时,所有次数都是1,当母串长度为0时,所有次数都是0.当母串子串都是0长度时,次数是1(因为都是空,相等)。
接着,如果子串的最后一个字母和母串的最后一个字母不同,说明新加的母串字母没有产生新的可能性,可以沿用该子串在较短母串的出现次数,所以dp(i)(j) = dp(i)(j-1)。
如果子串的最后一个字母和母串的最后一个字母相同,说明新加的母串字母带来了新的可能性,我们不仅算上dp(i)(j-1),也要算上新的可能性。那么如何计算新的可能性呢,其实就是在既没有最后这个母串字母也没有最后这个子串字母时,子串出现的次数,我们相当于为所有这些可能性都添加一个新的可能。所以,这时dp(i)(j) = dp(i)(j-1) + dp(i-1)(j-1)。
下图是以rabbbit和rabbit为例的矩阵示意图。计算元素值时,当末尾字母一样,实际上是左方数字加左上方数字,当不一样时,就是左方的数字。
public static int numDistinct(String S, String T) {
if (S == null || T == null || S.length() == 0 || T.length() == 0 || S.length() < T.length())
return 0;
int col = S.length() + 1;
int line = T.length() + 1;
int[][] array = new int[line][col];
Arrays.fill(array[0], 1);
for (int i = 1; i < line; i++) {
array[i][0] = 0;
}
for (int i = 1; i < line; i++) {
for (int j = 1; j < col; j++)
array[i][j] = array[i][j - 1] + (S.charAt(j - 1) == T.charAt(i - 1) ? array[i - 1][j - 1] : 0);
}
return array[line - 1][col - 1];
}