zoukankan      html  css  js  c++  java
  • leetcode --distinct subsequences

    1.题目描述

    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.

    2.解题思路

    这道题一看就感觉能用动态规划解,但是想了好久还是没有想到一个动态规划的最优子结构,没办法, 只好在discuss里面找,总算看明白了递推式:

    DP: let F(i, j) denote the number of ways for S[0]…S[i] contain T[0]..T[j], Then F(n-1, m-1) is our answer and we have
    if(S[i] != T[j])  F(i, j) = F(i-1, j);
    if(S[i] == T[j]) F(i, j) = F(i-1, j-1) + F(i-1, j);

    自以为理解了,然后写出了下面这个动态规划的算法,写的过程就觉得繁冗拖沓,不太对劲,果然,写完了小数据及可以通过,但是大数据集过不了(TLE),仔细分析发现自己的算法是O(N2)的,空间效率是O(MN),是个很不好的代码

     
    class Solution {
    public:
        int numDistinct(string S, string T) {
            // Start typing your C/C++ solution below
            // DO NOT write int main() function
            if(S.empty()||T.empty())return 0;
            
            vector<vector<int> > dp;
            vector<int>cur;
            cur.assign(T.size(),0);
            dp.push_back(cur);
            
            if(S[0]==T[0])dp[0][0]=1;
            
            for(int i = 1;i<S.size();++i)
            {
                vector<int> dpi;
                dpi.assign(T.size(),0);
                for(int k = 0;k<=i;++k)
                {
                    if(S[k]==T[0])dpi[0]=dpi[0]+1;
                }
                for(int j = 1;j<T.size();++j)
                {
                    dpi[j] = dp[i-1][j] +((S[i]==T[j])?dp[i-1][j-1]:0);
                }
                
                dp.push_back(dpi);
            }
            
            return dp[S.size()-1][T.size()-1];
            
        }
    };

    查看上述代码很容易发现,代码时间复杂度瓶颈在于求dp[i][0]的代价。实际上,我考虑不周,不需要这么每次都遍历一下。稍微修改一下代码即可,如下,时间复杂度变成了O(MN):

    class Solution {
    public:
        int numDistinct(string S, string T) {
            // Start typing your C/C++ solution below
            // DO NOT write int main() function
            if(S.empty()||T.empty())return 0;
            
            vector<vector<int> > dp;
            vector<int>cur;
            cur.assign(T.size(),0);
            dp.push_back(cur);
            
            if(S[0]==T[0])dp[0][0]=1;
            
            for(int i = 1;i<S.size();++i)
            {
                vector<int> dpi;
                dpi.assign(T.size(),0);
                dpi[0]=dp[i-1][0]+(S[i]==T[0]);
            
                for(int j = 1;j<T.size();++j)
                {
                    dpi[j] = dp[i-1][j] +((S[i]==T[j])?dp[i-1][j-1]:0);
                }
                
                dp.push_back(dpi);
            }
            
            return dp[S.size()-1][T.size()-1];
            
        }
    };

    结果如下:

    image

    后来受了网上的启发,发现了一个可以省空间的做法,那就是循环利用,设F[i][j]表示S[0…i]中包含T[0…j]的总个数,如果求得了F[i]中每一个元素,那么F[i+1]是可以递推出来的,递推公式在上面已经写过,对于F[i+1]中的每一个元素F[i+1][j],它只跟F[i][j]以及F[i][j-1]有关,如此这般,我们就可以循环利用F,于是,若我们在第i轮遍历求的了Fi,那么Fi+1可以逐步求出来,由于每一步Fi[j]会被替换,所以需要保存下来。

    class Solution {
    public:
        int numDistinct(string S, string T) {
            // Start typing your C/C++ solution below
            // DO NOT write int main() function
            vector<int> F;
            F.assign(T.size(),0);
            if(S[0]==T[0])F[0] =1;
            
            for(int i = 1;i<S.size();++i)
            {
                int pre=F[0];
                F[0] = F[0]+(S[i]==T[0]);
                int cur;
                for(int j =1;j<T.size();++j)
                {
                    cur = F[j];
                    F[j] = F[j]+(S[i]==T[j])*pre;
                    pre = cur;
                }
            }
            
            return F[T.size()-1];
            
        }
    };

    于是,空间需求降到了O(M)。由于申请空间的次数减少,程序运行时间也减少了,如下:

    image

  • 相关阅读:
    [读书笔记]子查询
    [读书笔记]SQL约束
    [转载]NoSQL数据库的基础知识
    利用C#实现对excel的写操作
    [转载]SQL Server内核架构剖析
    利用花生壳和IIS发布网页过程
    [读书笔记]ASP.NET的URL路由引擎
    [翻译]比较ADO.NET中的不同数据访问技术(Performance Comparison:Data Access Techniques)
    [正则表达式]基础知识总结
    CF623E Transforming Sequence
  • 原文地址:https://www.cnblogs.com/obama/p/3254963.html
Copyright © 2011-2022 走看看