BZOJ 3998 后缀自动机 解题报告

Description

对于一个给定长度为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<=10^9

【解题报告】
后缀自动机裸题。

代码如下:

/**************************************************************
    Problem: 3998
    User: onepointo
    Language: C++
    Result: Accepted
    Time:5184 ms
    Memory:124824 kb
****************************************************************/

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm> 
using namespace std;
#define N 500010

int T,n,K;
char ch[N];
struct sam
{
    int last,cnt;
    int a[N<<1][26],fa[N<<1],mx[N<<1],val[N<<1],sum[N<<1];
    int v[N],q[N<<1];
    sam(){last=++cnt;}
    void extend(int c)
    {
        int p=last,np=last=++cnt;
        mx[np]=mx[p]+1;val[np]=1;
        while(!a[p][c]&&p) a[p][c]=np,p=fa[p];
        if(!p) fa[np]=1;
        else
        {
            int q=a[p][c];
            if(mx[p]+1==mx[q]) fa[np]=q;
            else
            {
                int nq=++cnt;mx[nq]=mx[p]+1;
                memcpy(a[nq],a[q],sizeof(a[q]));
                fa[nq]=fa[q];
                fa[np]=fa[q]=nq;
                while(a[p][c]==q) a[p][c]=nq,p=fa[p];
            }
        }
    }
    void pre()
    {
        for(int i=1;i<=cnt;++i) v[mx[i]]++;
        for(int i=1;i<=n;++i) v[i]+=v[i-1];
        for(int i=cnt;i;--i) q[v[mx[i]]--]=i;
        for(int i=cnt;i;--i)
        {
            int t=q[i];
            if(T==1) val[fa[t]]+=val[t];
            else val[t]=1;
        }
        val[1]=0;
        for(int i=cnt;i;--i)
        {
            int t=q[i];sum[t]=val[t];
            for(int j=0;j<26;j++) sum[t]+=sum[a[t][j]];
        }
    }
    void dfs(int x,int K)
    {
        if(K<=val[x]) return;
        K-=val[x];
        for(int i=0;i<26;++i)
        if(int t=a[x][i])
        {
            if(K<=sum[t])
            {
                putchar(i+'a');
                dfs(t,K);
                return;
            }
            K-=sum[t];
        }
    }
}sam;

int main()
{
    scanf("%s",ch+1);
    n=strlen(ch+1);
    scanf("%d%d",&T,&K);
    for(int i=1;i<=n;++i)
        sam.extend(ch[i]-'a');
    sam.pre();
    if(K>sam.sum[1])puts("-1");
    else sam.dfs(1,K);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值