正题
大意
一个字符串,要求第k小的子串。
解题思路
先建立一个后缀自动机,然后用一个numinum_inumi表示第iii个节点的可以到达的点所表示的子串总和,然后从第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();//计算numGet_answer();
}