LG3975 [TJOI2015]弦论

题意

题目描述

为了提高智商,ZJY开始学习弦论。这一天,她在《 String theory》中看到了这样一道问题:对于一个给定的长度为n的字符串,求出它的第k小子串是什么。你能帮帮她吗?

输入输出格式

输入格式:

第一行是一个仅由小写英文字母构成的字符串s

第二行为两个整数t和k,t为0则表示不同位置的相同子串算作一个,t为1则表示不同位置的相同子串算作多个。k的意义见题目描述。

输出格式:

输出数据仅有一行,该行有一个字符串,为第k小的子串。若子串数目不足k个,则输出-1。

输入输出样例

输入样例#1: 复制
aabc
0 3
输出样例#1: 复制
aab
输入样例#2: 复制
aabc
1 3
输出样例#2: 复制
aa
输入样例#3: 复制
aabc
1 11
输出样例#3: 复制
-1

说明

数据范围

对于10%的数据,n ≤ 1000。

对于50%的数据,t = 0。

对于100%的数据,n ≤ 5 × 10^5, t < 2, k ≤ 10^9。

分析

首先建立SAM,不同字符串对应于不同的从初始节点开始的路径,然后根据题意分类:

  1. t为0则表示不同位置的相同子串算作一个,那么每个状态对应路径的字符串的出现次数只能算作1次。
  2. t为1则表示不同位置的相同子串算作多个,那么每个状态对应路径的字符串的出现次数等于它的end-pos集合大小,基数排序求拓扑序后更新即可。

时间复杂度\(O(|S|)\)

代码

把初始节点设成1,即让last=tot=1会方便许多。

#include<bits/stdc++.h>
#define rg register
#define il inline
#define co const
template<class T>il T read(){
    rg T data=0,w=1;rg char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') w=-1;ch=getchar();}
    while(isdigit(ch)) data=data*10+ch-'0',ch=getchar();
    return data*w;
}
template<class T>il T read(rg T&x) {return x=read<T>();}
typedef long long ll;
using namespace std;

co int N=5e5+5;
char s[N];
int n,t,k;
int ch[N*2][26],len[N*2],fa[N*2],siz[N*2],last=1,tot=1;
void extend(int c){
    int cur=++tot;
    len[cur]=len[last]+1,siz[cur]=1;
    int p=last;last=cur;
    for(;p&&!ch[p][c];p=fa[p]) ch[p][c]=cur;
    if(!p) fa[cur]=1;
    else{
        int q=ch[p][c];
        if(len[q]==len[p]+1) fa[cur]=q;
        else{
            int clone=++tot;
            len[clone]=len[p]+1,copy(ch[q],ch[q]+26,ch[clone]);
            fa[clone]=fa[q],fa[q]=fa[cur]=clone;
            for(;ch[p][c]==q;p=fa[p]) ch[p][c]=clone;
        }
    }
}
int c[N],a[N*2],sum[N*2];
int main(){
    scanf("%s",s+1),n=strlen(s+1);
    read(t),read(k);
    for(int i=1;i<=n;++i) extend(s[i]-'a');
    for(int i=1;i<=tot;++i) ++c[len[i]];
    for(int i=1;i<=n;++i) c[i]+=c[i-1];
    for(int i=1;i<=tot;++i) a[c[len[i]]--]=i;
    for(int i=tot;i;--i){
        if(t) siz[fa[a[i]]]+=siz[a[i]];
        else siz[a[i]]=1;
    }
    siz[1]=0;
    for(int i=tot;i;--i){
        sum[a[i]]=siz[a[i]];
        for(int c=0;c<26;++c)
            if(ch[a[i]][c]) sum[a[i]]+=sum[ch[a[i]][c]];
    }
    if(k>sum[1]) return puts("-1"),0;
    int now=1;k-=siz[now];
    while(k){
        int c=0;
        for(;k>sum[ch[now][c]];++c) k-=sum[ch[now][c]];
        putchar('a'+c);
        now=ch[now][c],k-=siz[now];
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/autoint/p/10678352.html
今日推荐