[TJOI2015]弦论,LuoguP3917,后缀自动机

本文探讨了如何利用后缀自动机解决复杂的字符串匹配问题,详细介绍了后缀自动机的构建过程,以及如何在自动机上进行有效的字符串搜索。文章通过一个具体的实例,展示了如何统计字符串中特定子串的出现次数,并提出了一种新颖的方法来解决这一问题。通过递归遍历和动态规划,该方法能够高效地找到所有可能的子串并计数。

正题

      Portal

      考虑建出后缀自动机,在上面不断往后跑,每当trans一遍的时候,可以保证跳到的等价类一定包含s+c这个状态。

       也就是说,其实通过什么途径到达当前的等价类其实并不重要。

       所以就可以分情况,当t=0时,一个等价类的出现次数就是1,当t=1时,一个等价类的出现次数就是endpos集合的大小。

       然后每一个节点统计一下自己可以到达的子串总数,就可以类似Trie树那样跑了。

      这一题很刺激,因为很少有自己想出来而且一发过的。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;

const int N=5e5+10;
char s[N];
struct sam{
	int link,next[26],len;
}sam[N<<1];
struct edge{
	int y,next;
}e[N<<1];
int first[N<<1];
int len=0,tot,last,type,k,t[N<<1],ansl,all[N<<1];
char ans[N];
bool vis[N<<1];

void insert(int c){
	int now=++tot;sam[now].len=sam[last].len+1;t[now]=1;
	while(last!=-1 && !sam[last].next[c]) sam[last].next[c]=now,last=sam[last].link;
	if(last==-1) sam[now].link=0;
	else{
		int temp=sam[last].next[c];
		if(sam[temp].len==sam[last].len+1) sam[now].link=temp;
		else{
			int np=++tot;sam[np]=sam[temp];sam[np].len=sam[last].len+1;
			sam[temp].link=sam[now].link=np;
			while(last!=-1 && sam[last].next[c]==temp) sam[last].next[c]=np,last=sam[last].link;
		}
	}
	last=now;
}

void ins(int x,int y){
	static int len=0;
	e[++len]=(edge){y,first[x]};first[x]=len;
}

void dfs(int x){
	for(int i=first[x];i!=0;i=e[i].next){
		dfs(e[i].y);
		t[x]+=t[e[i].y];
	}
}

void get_all(int x){
	if(vis[x]) return ;
	vis[x]=true;all[x]=t[x];
	for(int i=0;i<26;i++) if(sam[x].next[i]){
		get_all(sam[x].next[i]);
		all[x]+=all[sam[x].next[i]];
	} 
}

bool get_ans(int x){
	if(t[x]>=k) {ans[++ansl]='\0';return true;}
	k-=t[x];
	for(int i=0;i<26;i++) if(sam[x].next[i]){
		if(all[sam[x].next[i]]>=k) {
			ans[++ansl]='a'+i;
			return get_ans(sam[x].next[i]);
		}
		else k-=all[sam[x].next[i]];
	}
	return false;
}

int main(){
	scanf("%s",s+1);len=strlen(s+1);
	scanf("%d %d",&type,&k);
	sam[last].link=-1;
	for(int i=1;i<=len;i++) insert(s[i]-'a');
	if(type==0) for(int i=1;i<=tot;i++) t[i]=1;
	else{
		for(int i=1;i<=tot;i++) ins(sam[i].link,i);
		dfs(0);t[0]=0;
	}
	get_all(0);ansl=-1;
	if(get_ans(0)) printf("%s",ans);
	else printf("-1");
}

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值