jzoj4025-找回密码【后缀自动机】

正题


大意

一个字符串,要求第k小的子串。


解题思路

先建立一个后缀自动机,然后用一个 n u m i num_i numi表示第 i i i个节点的可以到达的点所表示的子串总和,然后从第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();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值