1. 程式人生 > >弦論(tjoi2015,bzoj3998)(sam)

弦論(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);
}