您的位置:首页 > 其它

bzoj3998 [TJOI2015]弦论

2016-12-02 00:05 134 查看
【题意】

给出一个长为n的字符串,求第k小子串。

[1]不同位置的相同子串算作1个

[2]不同位置的相同子串算作多个

【数据范围】

n<=500000

【思路】

[1]后缀数组经典应用,但时间复杂度O(n log n),不能通过此题。

    使用后缀自动机。后缀自动机的性质:后缀自动机是个DAG图,一个子串(互不相同)与一条从起点出发到某点的某路径一一对应。故在后缀自动机上dp出路径条数并按字典序贪心。

[2]使用后缀自动机。预处理parent树,对于新添加的结点(即原本n+1个结点之外的结点),它会带来重复计数,故此点权值为0,其余点权值为1。在parent树上dfs出子树的size(即子树上述点权的和)。关键性质:从起点走到该点的任意路径所唯一对应的子串在原字符串中出现的次数为size(它包含于size个前缀)。后面使用[1]的方法,DAG图dp并贪心即可。

【时间复杂度】

O(26n)

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 1000010
#define ll long long
using namespace std;

struct edge{int x, y, next;}a
;
int n, temp, k, root, last, sum, ch
[26], mx
, fa
, flag
, p
, l, size
;
ll f
;
char s
;

void sam_ins(int i){
int c=s[i]-'a', p=last, np=++sum; last=sum; flag[np]=1;
mx[np]=mx[p]+1;
while(p&&!ch[p][c]){ch[p][c]=np; p=fa[p];}
if(!p)fa[np]=root;
else{
int q=ch[p][c];
if(mx[q]==mx[p]+1)fa[np]=q;
else{
int nq=++sum; mx[nq]=mx[p]+1; flag[nq]=0;
memcpy(ch[nq], ch[q], sizeof(ch[nq]));
fa[nq]=fa[q]; fa[q]=fa[np]=nq;
while(p&&ch[p][c]==q){ch[p][c]=nq; p=fa[p];}
}
}
}

void dp(int x){
f[x]=size[x];
for(int i=0; i<=25; i++)if(ch[x][i]){
if(f[ch[x][i]]==-1)dp(ch[x][i]);
f[x]+=f[ch[x][i]];
}
}
void check(int x){
if(k<=size[x])return; k-=size[x];
for(int i=0; i<=25; i++)if(ch[x][i]){
if(k>f[ch[x][i]])k-=f[ch[x][i]];
else{printf("%c", i+'a'); check(ch[x][i]); return;}
}
printf("-1");
}

void add(int x, int y){a[++l].x=x; a[l].y=y; a[l].next=p[x]; p[x]=l;}
void getsize(int x){
size[x]=flag[x];
for(int i=p[x]; i; i=a[i].next){
getsize(a[i].y); size[x]+=size[a[i].y];
}
}

int main(){
scanf("%s", s+1); n=strlen(s+1);
scanf("%d%d", &temp, &k);
root=last=sum=1;
for(int i=1; i<=n; i++)sam_ins(i);
if(!temp){
for(int i=1; i<=sum; i++)size[i]=1;
memset(f, -1, sizeof(f)); dp(1);
k++; check(1);
}else{
l=0; memset(p, 0, sizeof(p));
for(int i=2; i<=sum; i++)add(fa[i], i);
getsize(1); size[1]=1;
memset(f, -1, sizeof(f)); dp(1);
k++; check(1);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: