AtCoder regular contest 081 E - Don't Be a Subsequence

博客详细解析了如何找到给定字符串的字典序最小且非其子序列的最短字符串问题。首先对原字符串进行倒序处理,通过划分区间并确定答案字符串的长度。接着,通过贪心策略选择每个位置的字符,确保每个字符都不是前缀子序列的一部分,从而构造出满足条件的字符串。

原题链接:http://arc081.contest.atcoder.jp/tasks/arc081_c

题目大意:给出一个小写字母构成的字符串,求满足2个条件的字典序最小的字符串:

1.不是这个字符串的子序列;

2.长度最短;

先把整个给定的字符串S倒序处理一下,每次集齐26个字母就把set清空并把这一区间划归为同一组

(令区间范围为 [ Li , Ri ] )( 1<=i<=tot) 

则可以知道长度为tot的所有字符串都为S的子序列,而且存在长度为tot+1的字符串不是S的子序列,所以答案串的长度为tot+1

令串S从第 i 个字符开始的后缀为 suffix ( i )

令答案串为ans

令答案串到第 i 个字符开始的前缀第一次作为S的子序列出现的位置为 pos( i )

显然ans[ 1 ]是[ 1 , L1 )区间内没出现过的字符,否则,以这个字符为开头的长度为tot+1的字符串都是S的子序列

然后当前的问题就转化为了 获得长度为tot且不是 suffix( pos( 1 ) )的子序列的字典序最小的字符串,显然除了第一组,其他的分组仍然没有变,这样我们所求的ans[2]一定是[ pos(1) , L2)区间内没出现过的字符,否则,以这个字符ans[2]的长度为tot的字符串都是S的子序列,

如此递推处理,就会得到一个长度为tot的字符串,最后一个字符只要是[ pos(tot) ,len]区间内没有出现过的字符就是满足条件的。

显然每次在取答案串的字符的时候就可以贪心取最小的字符就可以保证是字典序最小的答案串。

代码:

#include <cmath>
#include <queue>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <map>
#include <bits/stdc++.h>
#define mp make_pair
using namespace std;

constexpr int maxn = 2000010;
char s[maxn];

set <int> S;
int nextt[maxn][30];
int now[30];
int now_tot[maxn];

void doit(){
    scanf("%s",s);
    int len=strlen(s);
    int tot=0;
    for (int i=len;i>=1;i--)
    {
        S.insert( s[i-1]-'a' );
        now_tot[i]=tot;
        if (S.size()==26)
        {
            tot++;
            S.clear();
        }
    }
    if (tot==0)
        {
        for (int i=0;i<26;i++)
            if (S.count (i )==0)
                {
                    printf("%c",i+'a');
                    return ;
                }
        }
    memset(now,0,sizeof(now));
    for (int i=len;i>=1;i--)
    {
        for (int j=0;j<26;j++)
            nextt[i][j]=now[j];
        now[ s[i-1]-'a' ]=i;
    }
    int tmp=0;
    for (int i=0;i<26;i++)
        if (S.count (i )==0)
        {
            tmp=now[i];
            break;
        }
    printf("%c",s[tmp-1]);
    for (int i=1;i<tot;i++)
        {
            for (int j=0;j<26;j++)
                if (now_tot [ nextt[ tmp ][ j ] ]==now_tot [ tmp ] -1 )
                    {
                        tmp=nextt[ tmp ][ j ];
                        printf("%c",s[tmp-1]);
                        break;
                    }
        }
    for (int i=0;i<26;i++)
        if (nextt[ tmp ][ i ]==0)
            {
                printf("%c",i+'a');
                break;
            }
}


int main(){
    doit();
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值