Given a string S and a string T, count the number of distinct subsequences of T in S.
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串。其相对位置不能改变。
方案一:
通过DFS来求解,当s[i] == t[j]的时候,我们可以选择s[i]深层次递归,也可以不选择s[i]深层次递归。但是会出现超时结果。
class Solution {
public:
int numDistinct(string S, string T) {
CountDistinct(S,0,T,0);
return count;
}
void CountDistinct(string S,int index1,string T,int index2){
if(index2 >= T.size()){
count++;
return;
}
//找到等于T[index2]的点。但在循环中一定要添加变量范围限制
while(index1 < S.size() && S[index1] != T[index2])
index1++;
if(index1 >= S.size())
return;
CountDistinct(S,index1+1,T,index2+1);//选择这个点
CountDistinct(S,index1+1,T,index2); //不选这个点
}
private:
int count = 0;
};
方案二:
像这种递归超时的情况,可尝试用动态规划来求解。
动态规划难就难在找递推关系上。没有一个正确的方法,就会陷到死角去。
问1:当s[i]和t[j]不相等的时候,t[j]要和s[i]以后的比较。
答:这种思维就错误了,我们用DFS的时候,是上面这种思路,但是动态规划,是将大问题化渐成小问题。因此当s[i]和t[j]不相等的时候,t[j]要和s[i]以后的比较,并且只利用与s[i-1]的比较结果就行了,不需要再往前找。因为当我们求解二维数组的时候,已经求解出了t[j]和s[i-2]等的关系。
如果不好想的话,就那t[m]和s[n]比较,当两个不相等的时候,就没有s[n+1]了,应该向前找。
问2:递推关系式是什么?
那就考察s[i]和t[j]相等和不相等的情况。
把这个递推条件设为S前i个字符通过删除字符得到T前j个字符的转换方法,用二维数组元素transArr[i][j]记录。
(1)若S[i]==T[j],说明把S[i]、T[j]分别加入S[0~i-1]和T[0~j-1]可以完全复制transArr[i-1][j-1]种转换方式。另外,S[0~i-1]本身(无需加入S[i])也能以transArray[i-1][j]种方式转换为T[0~j]。
transArr[i][j] = transArr[i-1][j-1]+transArr[i-1][j];
(2)若S[i]!=T[j],说明S[i]无法用于转换为T[0~j],T[j]需要从S[0~i-1]中获取,因此S[0~i]与S[0~i-1]无异。
transArr[i][j] = transArr[i-1][j];
有了这两个关系式,才能进行下一步的计算。
问3:边界条件是什么?
递推关系一定要有个其实值,不然求解的时候,就没法向后延伸。我们思考下,当T为空串的时候,S为任意长度,那么删除S所有元素,就能变成T了。也就是边界条件为1。
class Solution {
public:
int numDistinct(string S, string T) {
if(S.size() < T.size() || T.size() == 0)
return 0;
int **arr = new int*[S.size()+1];
for(int i = 0;i <= S.size();i++)
arr[i] = new int[T.size()+1];
for(int i = 0;i <= T.size();i++)
arr[0][i] = 0;
for(int i = 0;i <= S.size();i++)
arr[i][0] = 1;
for(int i = 1;i <= S.size();i++){
for(int j = 1;j <= T.size();j++){
if(S[i-1] == T[j-1]) //数组中和字符串中坐标不一致,应小心
arr[i][j] = arr[i-1][j-1]+arr[i-1][j];
else
arr[i][j] = arr[i-1][j];
}
}
return arr[S.size()][T.size()];
}
};