题目描述:
Given a string S and a string T, count the number of distinct subsequences of S which equals T.
A subsequence of a string is a new string which is formed from the original string by deleting some (can be none) of the characters without disturbing the relative positions of the remaining characters. (ie, "ACE"
is a subsequence of "ABCDE"
while "AEC"
is not).
Here is an example:
S = "rabbbit"
, T = "rabbit"
Return 3
.
给定两个字符串s和t,求出在s中有多少个子序列能够构成t
问题是求出答案的数量,是一道典型的动态规划题
思路:
创建一个二维数组dp[i][j],其中dp[i][j]代表在s[0...j]中拥有构成t[0...i]的数量
1.dp[0][j]=1,因为""空字符串是任何一个字符串的子序列,并且仅有一种组成方式,即删除所有的s中的字符。
2.当i>0时,分为以下两种情况,
2.1如果t[i]!=s[j],此时忽略掉s[j],在s[0...j]中和s[0...j-1]中拥有相同构成t[0..i]的数量,因此dp[i][j] = dp[i][j-1]。
2.2如果t[i]==s[j],这时说明t[i]和s[j]是匹配的,dp[i][j] = dp[i][j-1]+dp[i-1][j-1]。
代码如下:
public int numDistinct(String S, String T) {
int sl = S.length();
int tl = T.length();
int [][] dp = new int[tl+1][sl+1];
for(int i=0; i<=sl; ++i){
dp[0][i] = 1;
}
for(int t=1; t<=tl; ++t){
for(int s=1; s<=sl; ++s){
if(T.charAt(t-1) != S.charAt(s-1)){
dp[t][s] = dp[t][s-1];
}else{
dp[t][s] = dp[t][s-1] + dp[t-1][s-1];
}
}
}
return dp[tl][sl];
}
优化:
在leetcode中,开辟二位数组会产生memory limit exceeded,
在上面的代码中,每一次更新二维数组中的值,仅用到了上一行的值和这一行的值,因此可以将二维数组变成两个一维数组。
代码如下:
public int numDistinct_sdp(String S, String T) {
int sl = S.length();
int tl = T.length();
int [] preComb = new int[sl+1];
int [] comb = new int[sl+1];
for(int i=0; i<=sl; i++)
preComb[i] = 1;
for(int t=1; t<=tl; ++t){
for(int s=1; s<=sl; ++s){
if(T.charAt(t-1) != S.charAt(s-1)){
comb[s] = comb[s-1];
}else{
comb[s] = comb[s-1] + preComb[s-1];
}
}
for(int i=0; i<=sl; ++i){
preComb[i] = comb[i];
}
}
return preComb[sl];
}
上面的代码在leetcode中仍然可以进一步优化。
public int numDistinct(String s, String t) {
int n1 = s.length(), n2 = t.length();
int[] dp = new int[n2 + 1];
dp[0] = 1;
for(int i = 1; i <= n1; ++i){
int pre = dp[0];
for(int j = 1; j <= n2; ++j){
int temp = dp[j];
if(s.charAt(i - 1) == t.charAt(j - 1)){
dp[j] = pre + dp[j];
}
pre = temp;
}
}
return dp[n2];
}