[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,求S的子串中与T相同的个数。注意子串是在原字符串中删除某些字符得到的字符串。如“ACE”是“ABCDE”的子串,而“AEC”不是,因为“AEC”把原字符串中的顺序改变了。

  很明显这是一道动态规划的题。首先需要找到递推关系。

  假设原字符S的长度为i,子字符串T的长度为j,用dp[i-1][j-1]表示S中含T的个数。例如:S="r",T="r",d[1-1][1-1] = d[0][0] = 1;S="ra",T="r",d[1][0] = 1。对于S="rabbbit",T="rabbit",可以得到一个如下的二维数组:(如下第三个图)

  先看第一个图,对于dp[0][0],即S="r",T="r",dp[0][0]=1,若第一个字符不等,则dp[0][0]=0;对于dp[0][1],即S="ra",T="r",dp[0][1]=1;同理可以得出第一行都是1。

  

  如下图,当i>j时,dp[i][j]=0,因此,对于第一列(i=0),除了dp[0][0]需要根据S[0]是否等于T[0]来确定之外,其他的dp[0][j]都为0。

  

  填充整个二维数组。对于i=j,dp[i][j]不会超过1。

  

  从这个二维数组可以看出,可以得出如下的递推关系(注意这个i,j):

    

  构造一个二维数组来实现算法:

class Solution {
public:
    int numDistinct(string S, string T) {
        int ls = S.length();
        int lt = T.length();
        if(lt > ls || ls == 0)return 0;
		
		static int dp[1000][1000] = {0};
        int i,j;
        for(i = 0; i < lt; i++)
            for(j = 0; j < ls; j++)
                dp[i][j] = 0;
         
        if(S[0] == T[0]){
            for(j = 0; j < ls; j++){
                dp[0][j] = 1;
            }
        }
        for(i = 1; i < lt; i++){
            for(j = i; j < ls; j++){
                if(T[i] == S[j])dp[i][j] = dp[i][j-1] + dp[i-1][j-1];
                else dp[i][j] = dp[i][j-1];
            }
        }
        int r = dp[lt-1][ls-1];
        return r;
    }
};

  这样提交之后发现"ccc", "c"这个case通不过,看来对于i=0的情况还要特殊处理。修改之后,如下,但是会超时。所以只能想别的办法。

public:
    int numDistinct(string S, string T) {
        int ls = S.length();
        int lt = T.length();
        if(lt > ls || ls == 0)return 0;
		
		static int dp[11000][11000] = {0};
        int i,j;
        for(i = 0; i < lt; i++)
            for(j = 0; j < ls; j++)
                dp[i][j] = 0;
         
        if(S[0] == T[0])dp[0][0] = 1;
		for(j = 1; j < ls; j++){
			if(S[j] == T[0])
				dp[0][j] = dp[0][j-1] + 1;
			else
				dp[0][j] = dp[0][j-1];
		}
        
        for(i = 1; i < lt; i++){
            for(j = i; j < ls; j++){
                if(T[i] == S[j])dp[i][j] = dp[i][j-1] + dp[i-1][j-1];
                else dp[i][j] = dp[i][j-1];
            }
        }
        int r = dp[lt-1][ls-1];
        return r;
    }

  下面这样处理比较好,就不需要对i=0的情况做特殊处理,相当于在S和T前加一个空字符。初始化的时候dp[0][j]=1(0<=j<=S.size());dp[i][0]=0(1<=i<=T.size())。

  

  下面是把二维数组变成一位数组来处理。因为在上面的实现算法中填充二维数组是一行一行的进行的,所以可以将二维数组“压缩”成一维的。为了让数组更小,我们需要把上面途中的维度给调换过来。在更新数组时方向也改变了,从右往左,如图:

  

  递推关系如下:

  用N[T.length()]表示子序列个数数组。例如在i=1时,N={1,1,0,0,0,0,0},N[1]表示S[0-1]中含T[0-1]子串的个数。再如i=4,N={1,1,1,2,1,0,0},N[3]表示S[0-4]含T[0-3]的子串的个数。不过这个每一行的递推是从从右到左的。初始情况N[0] = 1,N[1-T.size()]=0。

int numDistinct(string S, string T) {
        int ls = S.length();
        int lt = T.length();
        if(lt > ls || ls == 0)return 0;
        int dp[200];
        int i,j;
        dp[0] = 1;
        for( j = 1; j <= lt; j++){
            dp[j] = 0;
        }
        for(i = 1; i <= ls; i++){
            for( j = lt; j >= 1; j--){
                dp[j] += (S[i-1] == T[j-1])*dp[j-1];
            }
        }
        return dp[lt];
    }

  

参考:http://stackoverflow.com/questions/20459262/distinct-subsequences-dp-explanation

        http://fisherlei.blogspot.com/2012/12/leetcode-distinct-subsequences_19.html

        http://blog.csdn.net/abcbc/article/details/8978146

  

  

原文地址:https://www.cnblogs.com/zhutianpeng/p/4288340.html