【AtCoder Regular Contest 081 E】Don't Be a Subsequence

贪心。

题目大意:

给定一个字符串,你需要输出不是这个字符串的子序列的最短串。如果有长度相同的,输出字典序最小的。

题解:

我们首先求出最短串的长度。

如果长度为 $k$,那么前 $k-1$ 个位置无论选什么都存在。说明前面至少有 $k-1$ 个包含 a..z 所有字符的不相交的段。

我们从左往右扫,看看这样的段最多有多少个即可。具体做法是记录当前字符出现的集合,如果所有字符都出现了,则段数加一并清空集合。

然后考虑确定这个字符串。

我们预处理出每个位置后面的 a..z 分别最早出现的位置,和每个后缀的上述连续段长度。

然后按顺序枚举每一位字符,如果这个字符在后面最早出现的位置的后缀是满的,则不可选。如果不满,则可以选。

这样贪心即可求出字典序最小的字符串。

时间复杂度 $O(n)$。

Code:

#include<cstdio>
#include<cstring>
const int N=2e5+7;
char s[N];
int t,l=1,n,nxt[N][26],qwq[26],qaq[N];
int main(){
    scanf("%s",s+1);
    n=strlen(s+1);
    for(int i=1;i<=n;++i)if((t|=1<<(s[i]-97))==67108863)++l,t=0;
    for(int i=0;i<26;++i)qwq[i]=n+1;
    t=0;
    for(int i=n;~i;--i){
        memcpy(nxt[i],qwq,sizeof qwq);
        qwq[s[i]-97]=i;
        qaq[i]=qaq[i+1];
        if((t|=1<<(s[i]-97))==67108863)t=0,++qaq[i];
    }
    int nw=0;
    for(int i=1;i<l;++i){
        for(int c=0;c<26;++c){
            int to=nxt[nw][c];
            if(to<=n&&qaq[to+1]<l-i){
                putchar(c+97);
                nw=to;
                break;
            }
        }
    }
    t=0;
    for(int i=nw+1;i<=n;++i)t|=1<<(s[i]-97);
    for(int i=0;i<26;++i)if(t>>i&1^1)return putchar(i+97),0;
    return 0;
}