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; }
没有人不辛苦,只有人不喊疼