码迷,mamicode.com
首页 > 其他好文 > 详细

[TJOI2015]弦论

时间:2019-01-05 12:07:10      阅读:173      评论:0      收藏:0      [点我收藏+]

标签:swa   str   turn   lin   main   string   scan   ++   前缀   

我们先求出该字符串的\(SA\)\(Ht\)

然后分类讨论

\(T=0\)时,每次去掉\(Ht\)往后扫就行

\(T=1\)时,我们考虑\(lcp\)对答案的影响

既然用到\(lcp\),那就要用\(ST\)表维护

左端点固定时,随右端点的增大,区间\(\min\)单调不升

我们就可以用二分+限制右端点的方式统计某个后缀的前缀的贡献

循环遍历每个后缀时,由于\(Ht\)部分已经统计过对答案的贡献,就不用再计算了,从\(Ht+1\)的位置继续向后统计即可

#include"cstdio"
#include"cstring"
#include"iostream"
#include"algorithm"
using namespace std;

const int MAXN=5e5+5;

int ln,maxn,p,t;
int SA[MAXN],id[MAXN],rnk[MAXN],bnk[MAXN],Ht[MAXN];
char ch[MAXN];
int ST[19][MAXN],lg[MAXN];

void shel()
{
    for(int i=1;i<=ln;++i) ++bnk[rnk[id[i]]];
    for(int i=1;i<=maxn;++i) bnk[i]+=bnk[i-1];
    for(int i=1;i<=ln;++i) SA[++bnk[rnk[id[i]]-1]]=id[i];
    for(int i=0;i<=maxn;++i) bnk[i]=0;
    return;
}

void GetSA()
{
    for(int i=1;i<=ln;++i) rnk[i]=ch[i],id[i]=i,maxn=max(maxn,rnk[i]);
    shel();
    for(int k=1;k<ln;k<<=1){
        for(int i=1;i<=k;++i) id[i]=ln-k+i;
        int ct=k;
        for(int i=1;i<=ln;++i) if(SA[i]>k) id[++ct]=SA[i]-k;
        shel();swap(id,rnk);rnk[SA[1]]=1;
        for(int i=2;i<=ln;++i) rnk[SA[i]]=(id[SA[i]]==id[SA[i-1]]&&id[SA[i]+k]==id[SA[i-1]+k])?rnk[SA[i-1]]:rnk[SA[i-1]]+1;
        if(rnk[SA[ln]]==ln) return;
        maxn=rnk[SA[ln]];
    }return;
}

void GetHt()
{
    int k=0;
    for(int i=1;i<=ln;++i){
        if(rnk[i]==1) continue;
        if(k) --k;
        int ct=SA[rnk[i]-1];
        while(ct+k<=ln&&i+k<=ln&&ch[i+k]==ch[ct+k]) ++k;
        Ht[rnk[i]]=k;
    }return;
}

int Getmi(int l,int r){int tmp=lg[r-l+1];return min(ST[tmp][l+(1<<tmp)-1],ST[tmp][r]);}

void slv1()
{
    for(int i=1;i<=ln;++i) lg[i]=i>>lg[i-1]+1?lg[i-1]+1:lg[i-1];
    for(int i=1;i<=ln;++i) ST[0][i]=Ht[i];
    for(int j=1;j<=lg[ln];++j){
        int tmp=1<<j-1;
        for(int i=tmp<<1;i<=ln;++i){
            ST[j][i]=min(ST[j-1][i],ST[j-1][i-tmp]);
        }
    }for(int i=1;i<=ln;++i){
        int tmp=SA[i]+Ht[i];
        int ct=ln;
        while(1){
            if(tmp>ln) break;
            if(ct==i){
                if(t>ln-tmp+1){t-=ln-tmp+1;break;}
                for(int j=SA[i];j<=tmp+t-1;++j) putchar(ch[j]);
                return;
            }int l=i,r=ct;
            while(l<r){
                int mid=l+r+1>>1;
                if(Getmi(i+1,mid)>=tmp-SA[i]+1) l=mid;
                else r=mid-1;
            }ct=r;
            if(t>r-i+1) t-=r-i+1;
            else{
                for(int j=SA[i];j<=tmp;++j) putchar(ch[j]);
                return;
            }++tmp;
        }
    }puts("-1");
    return;
}

void slv2()
{
    int ct=1;
    while(t>ln-SA[ct]-Ht[ct]+1) t-=ln-SA[ct]-Ht[ct]+1,++ct;
    if(ct>ln){puts("-1");return;}
    for(int i=SA[ct];i<=SA[ct]+t+Ht[ct]-1;++i) putchar(ch[i]);
    puts("");
    return;
}

int main()
{
    scanf("%s",ch+1);ln=strlen(ch+1);
    GetSA();GetHt();scanf("%d%d",&p,&t);
    p?slv1():slv2();
    return 0;
}

[TJOI2015]弦论

标签:swa   str   turn   lin   main   string   scan   ++   前缀   

原文地址:https://www.cnblogs.com/AH2002/p/10223659.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!