标签:后缀自动机
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
Source
字典序第k小子串
SAM裸题
张天扬论文有详解
我这个傻逼求right集合的方法错了一直WA…
第二天才调好QAQ
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define MAXN 1000010
using namespace std;
int T,n,k;
char ch[MAXN>>1];
int c[MAXN],od[MAXN];
struct sam
{
int p,q,np,nq,last,cnt;
int len[MAXN],a[MAXN][26],fa[MAXN];
int right[MAXN],sum[MAXN];
sam()
{
last=++cnt;
}
inline void insert(int c)
{
p=last;np=last=++cnt;len[np]=len[p]+1;len[np]=len[p]+1;right[np]=1;
while (!a[p][c]&&p) a[p][c]=np,p=fa[p];
if (!p) fa[np]=1;
else
{
q=a[p][c];
if (len[q]==len[p]+1) fa[np]=q;
else
{
nq=++cnt;len[nq]=len[p]+1;
memcpy(a[nq],a[q],sizeof(a[q]));
fa[nq]=fa[q];fa[q]=fa[np]=nq;
while (a[p][c]==q) a[p][c]=nq,p=fa[p];
}
}
}
inline void init()//求出right集合和可到达状态总数sum
{
for (int i=1;i<=cnt;i++) c[len[i]]++;
int L=strlen(ch);
for (int i=1;i<=L;i++) c[i]+=c[i-1];
for (int i=cnt;i;i--) od[c[len[i]]--]=i;
for (int i=cnt;i;i--)
{
int x=od[i];
if (T) right[fa[x]]+=right[x];
else right[x]=1;
}
right[1]=0;
for (int i=cnt;i;i--)
{
int x=od[i];
sum[x]=right[x];
for (int j=0;j<26;j++) sum[x]+=sum[a[x][j]];
}
}
inline void dfs(int x,int nowk)
{
if (nowk<=right[x]) return;
nowk-=right[x];
for (int i=0;i<26;i++)
if (a[x][i])
{
if (nowk<=sum[a[x][i]])
{
putchar(i+‘a‘);
dfs(a[x][i],nowk);
return;
}
nowk-=sum[a[x][i]];
}
}
}sam;
int main()
{
scanf("%s",ch);
scanf("%d%d",&T,&k);
int l=strlen(ch);
for (int i=0;i<l;i++) sam.insert(ch[i]-‘a‘);
sam.init();
if (k>sam.sum[1]) puts("-1");
else sam.dfs(1,k);
}
版权声明:本文为博主原创文章,未经博主允许不得转载。
标签:后缀自动机
原文地址:http://blog.csdn.net/creationaugust/article/details/48106079