zoukankan      html  css  js  c++  java
  • 【TJOI2015】—弦论(后缀自动机)

    传送门

    第一次看见这样强行套两道题的

    首先字典序最小的子串肯定是在自动机上贪心

    而如果我们第一步走最小的转移

    那么第一个字符确定后无论剩下字符是什么,这些子串都绝对是最小的那些

    第二小的转移类似

    所以我们只需要在SamSam上统计每个点能dfsdfs到的子树大小(虽然这不是一棵树,但其实意思是一样的,毕竟这是一个DAGDAG

    其实可以按照lenlen排序后从小往大枚举SamSam上的点点,然后把每个点的所有转移的sizsiz加给它自己

    然后就像平衡树查找序列第kk大数一样

    枚举字符cc从小到大每一个儿子的转移,如果k>siz[son[k][c]]k>siz[son[k][c]]k=siz[son[k][c]]k-=siz[son[k][c]],走到第一个小等于的转移就走下去,因为要输出走法,所以在每次转移的时候输出转移字符就是了

    注意两种情况的不同做法,t=0t=0时是统计ParenttreeParent-tree上的sizesize

    把所有非克隆的点赋为11,排序后从大到小把sizsiz加给linklink

    t=1t=1时则相当于是统计每个点的endposendpos集合的大小

    因为任意的link[x]=link[y]link[x]=link[y]endpos[x]endpos[x]endpos[y]endpos[y]的任意元素都互不相同

    所以我们可以保证子树传上来一定就是自己的endposendpos集合大小

    t=0t=0时就不需要管出现过几次了,只需要把所有都赋为1就可以了

    注意最后会在后缀自动机上跑一次,就是统计所有子串的数量

    因为没理解自动机和ParenttreeParent-tree的区别和意义想了好久

    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    inline int read(){
        char ch=getchar();
        int res=0;
        while(!isdigit(ch))ch=getchar();
        while(isdigit(ch))res=(res<<3)+(res<<1)+(ch^48),ch=getchar();
        return res;
    }
    const int N=500005;
    struct node{
        int len,link,nxt[27];
    };
    #define max(a,b) (((a)>(b))?a:b)
    node st[N<<1];
    int tot,op,f[N<<1],t[N<<1],g[N<<1],k,siz[N<<1],last,ans;
    char a[N];
    inline void init(){
        last=tot=1;
    }
    inline void sa_extend(int c){
        int cur=++tot,p=last;last=cur;
        st[cur].len=st[p].len+1,siz[cur]=1;
        for(;p&&!st[p].nxt[c];p=st[p].link) st[p].nxt[c]=cur;
        if(!p)st[cur].link=1;
        else{
            int q=st[p].nxt[c];
            if(st[p].len+1==st[q].len)st[cur].link=q;
            else {
                int clo=++tot;
                st[clo]=st[q];           
                st[clo].len=st[p].len+1;     
                for(;p&&st[p].nxt[c]==q;p=st[p].link)st[p].nxt[c]=clo;
                st[q].link=st[cur].link=clo;                 
            }
        }
    }
    int main(){
        scanf("%s",a+1),init();
        int len=strlen(a+1);
        for(int i=1;i<=len;i++)sa_extend(a[i]-'a');
        op=read(),k=read();
        for(int i=1;i<=tot;i++)
            t[st[i].len]++;
        for(int i=1;i<=tot;i++)
            t[i]+=t[i-1];
        for(int i=1;i<=tot;i++)
            g[t[st[i].len]--]=i;
        if(op){
            for(int i=tot;i;i--)
                siz[st[g[i]].link]+=siz[g[i]];
        }
        else{
            for(int i=tot;i;i--)siz[g[i]]=1;
        }
        siz[1]=0;
        for(int i=tot;i;i--){
            f[g[i]]=siz[g[i]];
            for(int j=0;j<26;j++){
                if(st[g[i]].nxt[j])
                    f[g[i]]+=f[st[g[i]].nxt[j]];
            }
        }
        if(k>f[1]){
            cout<<-1;return 0;
        }
        int now=1;
        k-=siz[now];
        while(k){
            int p=0;
            while(k>f[st[now].nxt[p]])k-=f[st[now].nxt[p]],++p;
            now=st[now].nxt[p],putchar('a'+p),k-=siz[now];
        }
    }
    
  • 相关阅读:
    请多指教
    第九周作业总结
    win10系统开机输入密码黑屏解决方法
    第八周作业总结
    第七周作业总结
    第六周作业总结
    第五周作业总结
    第四周作业
    第三周作业
    2019年春季学期第二周作业
  • 原文地址:https://www.cnblogs.com/stargazer-cyk/p/10366380.html
Copyright © 2011-2022 走看看