LeetCode | Distinct Subsequences(不同的子序列)

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()];
    }
};


点赞