BZOJ 3998: [TJOI2015]弦论 SAM

title

BZOJ 3998

LUOGU 3975

Description

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

analysis

建出 \(SAM\) 求 \(K\) 大。

\(T=0\) 除了根以外的状态都代表 \(1\) 个串;

\(T=1\) 每个状态 \(fail\) 子树结束结点个数即为串的个数。

code

#include<bits/stdc++.h>
using namespace std;
const int maxn=5e5+10;

char buf[1<<15],*fs,*ft;
inline char getc() { return (ft==fs&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),ft==fs))?0:*fs++; }
template<typename T>inline void read(T &x)
{
    x=0;
    T f=1, ch=getchar();
    while (!isdigit(ch) && ch^'-') ch=getchar();
    if (ch=='-') f=-1, ch=getchar();
    while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
    x*=f;
}

int T,K,n;
struct SuffixAutoMaton
{
    int last,cnt;
    int ch[maxn<<1][26],fa[maxn<<1],l[maxn<<1];//数据范围不计算清楚真是害死人
    inline void extend(int c)
    {
        int p=last,np=++cnt;
        last=cnt;
        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;
            }
        }
        siz[np]=1;
    }

    int c[maxn],a[maxn<<1],siz[maxn<<1],sum[maxn<<1];//数据范围不计算清楚真是害死人
    inline void calc()
    {
        for (int i=1; i<=cnt; ++i) ++c[l[i]];
        for (int i=1; i<=n; ++i) c[i]+=c[i-1];//只需要计算出这个字符串实际长度的前缀即可
        for (int i=cnt; i; --i) a[c[l[i]]--]=i;//倒序搞
        for (int i=cnt; i; --i)//从小到大枚举,实际上在模拟parent树的DFS
        {
            int p=a[i];
            if (T==1) siz[fa[p]]+=siz[p];
            else siz[p]=1;
        }
        siz[1]=0;
        for (int i=cnt; i; --i)
        {
            int p=a[i];
            sum[p]=siz[p];
            for (int j=0; j<26; ++j) sum[p]+=sum[ch[p][j]];
        }
    }

    inline void dfs(int x,int K)
    {
        if (K<=siz[x]) return ;
        K-=siz[x];
        for (int i=0; i<26; ++i)
            if (int p=ch[x][i])
            {
                if (K<=sum[p])
                {
                    putchar(i+'a');
                    dfs(p,K);
                    return ;
                }
                K-=sum[p];
            }
    }
} SAM;

char s[maxn];
int main()
{
    scanf("%s",s+1);
    n=strlen(s+1);SAM.cnt=SAM.last=1;//1为跟,表示空串
    read(T);read(K);
    for (int i=1; i<=n; ++i) SAM.extend(s[i]-'a');
    SAM.calc();
    if (K>SAM.sum[1]) puts("-1");
    else SAM.dfs(1,K),puts("");
    return 0;
}
上一篇:[BZOJ5512][SAM]TJOI2019:甲苯先生和大中锋的字符串


下一篇:后缀自动机做题记录