正题
大意
一个字符串,要求第k小的子串。
解题思路
先建立一个后缀自动机,然后用一个 表示第 个节点的可以到达的点所表示的子串总和,然后从第1号点开始查找,判断一下找到第k小所在的节点后,然后查找输出。
代码
#include<cstdio>
#include<algorithm>
#include<cstring>
#define N 200010
#define Get(W) W>='a'?W-71:W-'A'
#define fuGet(W) W>=26?W+71:W+'A'
using namespace std;
int l,last,fail[N],next[N][56],tot,len[N],num[N];
int ls[N],v[N],head,tail,q[N];
long long sum;
char s[100001];
int New(int x,int y){
next[x][y]=++tot;
len[tot]=len[x]+1;
}
void bfs()
{
head=0;
q[1]=1;tail=1;v[1]=1;
do
{
int x=q[++head];
for (int i=0;i<52;i++)
if (next[x][i]&&!v[next[x][i]])
{
v[next[x][i]]=1;
q[++tail]=next[x][i];//建立顺序
}
}
while (head<tail);
for (;tail;tail--)
{
int x=q[tail];
for (int i=0;i<52;i++)
if (next[x][i])
{
ls[x]=i;
num[x]+=num[next[x][i]]+1;//按顺序计算答案
}
}
}
void Get_answer()
{
int x=1;
while (sum)
{
if (!ls[x]&&!next[x][0])break;
for (int i=0;i<=ls[x];i++)
if (next[x][i])
{
if (num[next[x][i]]+1>=sum||i==ls[x])//在当前节点的搜索树中
{
printf("%c",fuGet(i));
int y=next[x][i];
sum--;
if (sum) x=y;//进入该节点
break;
}
else sum-=num[next[x][i]]+1;//跳转下一棵
}
}
}
int main()
{
scanf("%s",s);
scanf("%lld",&sum);
l=strlen(s);
last=1;
len[1]=0;fail[1]=0;tot=1;
for (int i=0;i<l;i++)
{
int addc=Get(s[i]);
int k=last,j=0;
New(last,addc);//新建节点
last=tot;
for (j=fail[k];j;j=fail[j])
if (!next[j][addc])
next[j][addc]=last;//直接连接
else
{
int z=next[j][addc];
if (len[z]==len[j]+1)
fail[last]=z;//连接
else
{
New(j,addc);
for (int l=0;l<52;l++)
next[tot][l]=next[z][l];//继承指针
fail[tot]=fail[z];fail[z]=fail[last]=tot;//继承
for (int l=j;l;l=fail[l])
if (next[l][addc]==z) next[l][addc]=tot;//继承连接
}
break;
}
if (!j) fail[last]=1;/特判
}
bfs();//计算num
Get_answer();
}