LeetCode/不同的子序列II

给定一个字符串 s,计算 s 的 不同非空子序列 的个数

1. 动态规划(记录位置去重)

dp[i]为以i位置结尾的子序列数目,方便状态的转移
dp[i]= sum(dp[j]) ,j为上一次该字母出现位置到这一次字母出现的所有位置,从上一次出现位置开始主要为了去重
其实就是把该字母拼接到前面这些右边界确定的子序列上,形成新的子序列
同时要考虑该字母之前有没有出现过,否则其本身也可以作为一个子序列,即一开始dp[i]=1

class Solution {
public:
    int distinctSubseqII(string s) {
    //dp[i]表示以i为尾的子序列数目
    int mod_ = pow(10,9)+7;
    int sum = 0;//对所有边界子序列求和
    vector<int> dp(s.size());
    vector<int> map_(26,-1);//记录上一个26字母初始位置
    for(int i=0;i<s.size();i++){
        int index = map_[s[i]-'a'];//对应上一个该字母索引
        if(index==-1){ dp[i]=1; index=0;}//从未出现过,其本身作为子序列加一
        for(int j=index;j<i;j++){//遍历上一次出现位置后的所有位置,主要是为了去重
            dp[i]=(dp[i]+dp[j])%mod_;//拼上去累加
        }
        sum = (sum +dp[i])%mod_;
        map_[s[i]-'a'] = i;//更新该字母索引
    }
    return sum;
}
};

2. 动态规划优化(记录字符去重)

在方法一中,计算上一次该字母到该字母所有位置的子序列和,其实就是对以该字母结尾的子序列做了更新
设dp[i]是以字符i结尾的子序列数目,考虑新字符对所有类型字符拼接即可,同样起到了去重作用
状态转移方程dp[i] = sum(dp[j]) ,j为所有字符

class Solution {
public:
    int distinctSubseqII(string s) {
        vector<long>dp(26,0); //初始化容器为0
        int mod=1e9+7;  
        for(auto& ch:s){
            dp[ch-'a']=accumulate(dp.begin(),dp.end(),1l)%mod; //遍历字符串更新dp数组
        }
    return accumulate(dp.begin(),dp.end(),0L)%mod;//求和
    }
};

3. 动态规划(记录位置优化)

其实也就是找规律,省去了方法一求和的步骤

class Solution {
public:
    int distinctSubseqII(string s) {
    //找规律
    int len = s.size();
    int mod_ = pow(10,9)+7;
    vector<int> dp(len+1);
    dp[0] = 1;
    vector<int> map_(26,-1);//记录上一个26字母初始位置
    for(int i=0;i<s.size();i++){
        int index = map_[s[i]-'a'];//对应上一个该字母索引
        dp[i+1]=(dp[i]*2)%mod_;
        if(index>=0) dp[i+1]=(dp[i+1]-dp[index])%mod_;
        map_[s[i]-'a'] = i;//更新该字母索引
    }
    dp[len]--;
    if(dp[len]<0) dp[len]+=mod_;
    return dp[len];
}
};

4. 动态规划+找规律+去重(跟方法三一致,但思路更清晰)

class Solution {
public:
    int distinctSubseqII(string s) {
        int mod = (int)1e9 + 7;
        int n = s.size();
        vector<int> precount(26);//上一个以dp[i]字符结尾的新增个数,用于该次去重
        int cur = 1;//初始存在一个空串,用于跟遍历字符本身拼接
        for(int i=0;i<n;i++){
            int newcount = cur;
            cur = (((cur+newcount)%mod - precount[s[i]-'a'])%mod + mod)%mod;
            precount[s[i]-'a'] = newcount;
        }
        return (cur-1+mod)%mod;//减去空串
    }
};


posted @ 2022-06-10 23:23  失控D大白兔  阅读(62)  评论(0编辑  收藏  举报