zoukankan      html  css  js  c++  java
  • [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

      

      

  • 相关阅读:
    Google的Protobuf安装及使用笔记一
    std::list与std::map的排序比较
    svn库镜像
    daemontools中supervise系统进程监控
    memcached的客户端
    Memcached安装及使用
    eclipse下maven配置
    栈溢出
    堆排序
    redis使用笔记
  • 原文地址:https://www.cnblogs.com/zhutianpeng/p/4288340.html
Copyright © 2011-2022 走看看