AtCoder2698 Don't Be a Subsequence(dp+序列自动机)

考虑倒序dp

设计状态f[i]为到末尾的最小不存在子序列。

首先如果后面没有某个字母,那么答案就是1

不然的话必然要在首位加上一个字母。

考虑枚举所有情况,也就是第一个字母填什么,这样就是全部答案。

如果直接枚举后面每个位置更新,复杂度太高,我们想到维护一个后缀,表示每个字母在i和之后出现的第一个位置pos是哪就行

这是因为i-那个位置之间只有一个当前字母,由于越后面的状态答案越小,因此我们以这个字母开头,那么答案就是1+f[pos+1],因为f[pos+1]就是到终点的最小串,而我们第一个位置必须填一个字母

所以这种方案肯定成立,也是最优的。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pll;
const int N=2e5+10;
int nxt[N][26];
int f[N];
int main(){
    ios::sync_with_stdio(false);
    int i,j;
    string s;
    cin>>s;
    int n=s.size();
    s=" "+s;
    for(i=n;i>=1;i--){
        for(j=0;j<26;j++){
            if(s[i]-'a'==j){
                nxt[i][j]=i;
            }
            else{
                nxt[i][j]=nxt[i+1][j];
            }
        }
    }
    memset(f,0x3f,sizeof f);
    for(i=n;i>=1;i--){
        for(j=0;j<26;j++){
            if(!nxt[i][j]){
                f[i]=1;
                break;
            }
            else{
                f[i]=min(f[i],f[nxt[i][j]+1]+1);
            }
        }
    }
    int now=1;
    int ans=f[1];
    while(ans--){
        for(i=0;i<26;i++){
            if(f[now]==f[nxt[now][i]+1]+1){
                cout<<char('a'+i);
                now=nxt[now][i]+1;
                break;
            }
        }
    }
    for(i=0;i<26;i++){
        if(!nxt[now][i]){
            cout<<char('a'+i);
            break;
        }
    }
    cout<<endl;
    return 0;
}
View Code

 

posted @ 2021-04-22 10:24  朝暮不思  阅读(74)  评论(0编辑  收藏  举报