弦论(tjoi2015,bzoj3998)(sam)

对于一个给定长度为\(N\)的字符串,求它的第\(K\)小子串是什么。

Input

第一行是一个仅由小写英文字母构成的字符串\(S\)

第二行为两个整数\(T\)\(K\)\(T\)为0则表示不同位置的相同子串算作一个。\(T=1\)则表示不同位置的相同子串算作多个。\(K\)的意义如题所述。

Output

输出仅一行,为一个数字串,为第\(K\)小的子串。如果子串数目不足\(K\)个,则输出\(-1\)

Sample Input

aabc
0 3

Sample Output

aab

Hint

\(N<=5*10^5\)

\(T<2\)

\(K<=1e9\)

题意:

中文题面,不解释。

题解:

把串放进后缀自动机,然后处理一遍,如果\(T=0\),则所有点权为1;否则,把每个点的\(parent\)加上当前\(size\)。然后反向拓扑,像求第\(k\)大子串如这个一样求就行了。

#include<bits/stdc++.h>
using namespace std;
const int N=1000010;
char s[N];
int a[N],c[N];
void cmax(int &a,int b){
    a=max(a,b);
}
void cmin(int &a,int b){
    a=min(a,b);
}
struct SAM{
    int last,cnt;
    int size[N],ch[N][26],fa[N<<1],l[N<<1],sum[N];
    void ins(int c){
        int p=last,np=++cnt;last=np;l[np]=l[p]+1;
        for(;p&&!ch[p][c];p=fa[p])ch[p][c]=np;
        if(!p)fa[np]=1;
        else{
            int q=ch[p][c];
            if(l[p]+1==l[q])fa[np]=q;
            else{
                int nq=++cnt;l[nq]=l[p]+1;
                memcpy(ch[nq],ch[q],sizeof ch[q]);
                fa[nq]=fa[q];fa[q]=fa[np]=nq;
                for(;ch[p][c]==q;p=fa[p])ch[p][c]=nq;
            }
        }
        size[np]=1;
    }
    void build(char s[]){
        int len=strlen(s+1);
        last=cnt=1;
        for(int i=1;i<=len;++i)ins(s[i]-'a');
    }
    void calc(int op){
        memset(c,0,sizeof c);
        for(int i=1;i<=cnt;++i)c[l[i]]++;
        for(int i=1;i<=cnt;++i)c[i]+=c[i-1];
        for(int i=1;i<=cnt;++i)a[c[l[i]]--]=i;
        for(int i=cnt;i;--i){
            int p=a[i],f=fa[p];
            if(op){
                size[f]+=size[p];
            }else{
                size[p]=1;
            }
        }
        size[1]=0;
        for(int i=cnt;i;--i){
            int p=a[i];
            sum[p]=size[p];
            for(int j=0;j<26;++j){
                if(ch[p][j])sum[p]+=sum[ch[p][j]];
            }
        }
    }
    void find(int k){
        int p=1;
        size[0]=0;
        while(k){
            int a=0;
            while(k>sum[ch[p][a]]&&a<26){
                if (ch[p][a]) k-=sum[ch[p][a]];
                a++;
            }
            if(a>=26){
                puts("-1");
                return;
            }
            putchar('a'+a);k-=size[ch[p][a]];
            if(k<=0)return;
            p=ch[p][a];
        }
    }
}sam;
int main(){
    cin>>s+1;
    sam.build(s);
    int t,k;
    cin>>t>>k;
    sam.calc(t);
    sam.find(k);
}

猜你喜欢

转载自www.cnblogs.com/A1269180380/p/10098710.html
今日推荐