115. 不同的子序列

题目描述

给定一个字符串 S 和一个字符串 T,计算在 S 的子序列中 T 出现的个数。一个字符串的一个子序列是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字符串。(例如,"ACE" 是 "ABCDE" 的一个子序列,而 "AEC" 不是)
示例 1:
输入: S = "rabbbit", T = "rabbit"
输出: 3
解释:

如下图所示, 有 3 种可以从 S 中得到 "rabbit" 的方案。
(上箭头符号 ^ 表示选取的字母)

rabbbit
^^^^ ^^
rabbbit
^^ ^^^^
rabbbit
^^^ ^^^

示例 2:
输入: S = "babgbag", T = "bag"
输出: 5
解释:
如下图所示, 有 5 种可以从 S 中得到 "bag" 的方案。
(上箭头符号 ^ 表示选取的字母)

babgbag
^^ ^
babgbag
^^    ^
babgbag
^    ^^
babgbag
  ^  ^^
babgbag
    ^^^

思路

给定2个字符串s, t,求ts中出现的次数。要求可以是不连续的,但是ts中的顺序必须和t以前的一致。
状态定义:
dp[i][j]代表S[0~i-1]中等于T[0~j-1]的不同子串的个数。
动态规划计数类型递推公式,不重不漏。
递推关系式:
S[i-1]!= T[j-1]:dp[i][j] = dp[i-1][j](不选择S中的s[i-1]字符)
S[i-1]==T[j-1]:dp[i][j] = dp[i-1][j-1](选择S中的s[i-1]字符)+dp[i-1][j](不选择S中的s[i-1]字符)
初始状态:第0列:dp[i][0] = 1,第0行:dp[0][j] = 0(这里j!=0)

代码实现

class Solution {
public:
    int numDistinct(string S, string T) {

        int m = S.size();
        int n = T.size();
        //为了递推公式的方便,我们需要人为的创造长度为0时的情况,
        //所以这里的dp用的是长度而不是下标      
        vector<vector<unsigned> > dp(m+1, vector<unsigned>(n+1, 0));
        for(int i = 0; i <= m; i ++)
            dp[i][0] = 1;//对应把S中的字符全部删除完       
        for(int i = 1; i <= m; i ++)
        {
            for(int j = 1; j <= n; j ++)
            {  
                if(S[i-1] == T[j-1])
                    dp[i][j] = dp[i-1][j-1] + dp[i-1][j];
                else
                    dp[i][j] = dp[i-1][j];
            }
        }        
        return dp[m][n];       
    }

};

改进

动态规划用一维数组,在使用中不难发现该dp二维数组可以降维,注意改变数组元素值时由后往前

class Solution {
public:
int numDistinct(string S, string T) {
    int n=T.size();
    vector<unsigned> dp(n+1,0);
    dp[0]=1;
    for(int i=1;i < S.size()+1;i++)
    {
        for(int j=n;j>=1;j--)
        {
            if(S[i-1]==T[j-1])
                dp[j]=dp[j]+dp[j-1];
        }
    }
    return dp[n];
 }

};

posted on 2021-04-05 12:56  朴素贝叶斯  阅读(60)  评论(0编辑  收藏  举报

导航