Codeforces666E Forensic Examination【后缀自动机串定位,线段树合并】

题目描述:

在这里插入图片描述
洛谷链接

题目分析:

T [ 1... m ] T[1...m] T[1...m]建广义后缀自动机,可持久化线段树合并维护 endpos \texttt{endpos} endpos 集合中哪个串最多,以及最多的出现次数。
定位 S [ p l , p r ] S[p_l,p_r] S[pl,pr] T T T上的位置,具体地,把 S S S放到后缀自动机上匹配,求出每个前缀 S [ 1... i ] S[1...i] S[1...i]匹配的最长后缀长度 p l e n [ i ] plen[i] plen[i],及其在后缀自动机上的对应位置 p o s [ i ] pos[i] pos[i]。那么 p o s [ p r ] pos[p_r] pos[pr]的fail树上的祖先中深度最浅的 l e n [ x ] ≥ p r − p l + 1 len[x]\ge p_r-p_l+1 len[x]prpl+1 x x x 就对应包含了 S [ p l , p r ] S[p_l,p_r] S[pl,pr]
x x x 的线段树中 [ l , r ] [l,r] [l,r] 查询即可。

注意广义后缀自动机中会存在 len[x]=len[y],而 fail[y] = x,所以此时不能按照 len 桶排序来确定顺序。

Code:

#include<bits/stdc++.h>
#define maxn 100005
#define maxp maxn*35
using namespace std;
void read(int &a){
	char c;while(!isdigit(c=getchar()));
	for(a=c-'0';isdigit(c=getchar());a=a*10+c-'0');
}
int n,m,Q;
char s[maxn*5],t[maxn];
int rt[maxn],lc[maxp],rc[maxp],tot;
struct node{
	int sum,id;
	bool operator < (const node &p)const{return sum==p.sum?id>p.id:sum<p.sum;}
}f[maxp];
void ins(int &i,int l,int r,int x){
	if(!i) i=++tot;
	if(l==r) {f[i]=(node){1,x};return;}
	int mid=(l+r)>>1;
	x<=mid?ins(lc[i],l,mid,x):ins(rc[i],mid+1,r,x);
	f[i]=max(f[lc[i]],f[rc[i]]);
}
int merge(int x,int y,int l,int r){
	if(!x||!y) return x+y;
	int i=++tot,mid=(l+r)>>1;
	if(l==r) {f[i]=(node){f[x].sum+f[y].sum,f[x].id};return i;}
	lc[i]=merge(lc[x],lc[y],l,mid),rc[i]=merge(rc[x],rc[y],mid+1,r);
	f[i]=max(f[lc[i]],f[rc[i]]);
	return i;
}
node qry(int i,int l,int r,int x,int y){
	if(!i) return (node){0,x};
	if(x<=l&&r<=y) return f[i];
	int mid=(l+r)>>1; node ret=(node){0,x};
	if(x<=mid) ret=max(ret,qry(lc[i],l,mid,x,y));
	if(y>mid) ret=max(ret,qry(rc[i],mid+1,r,x,y));
	return ret;
}
int fa[maxn]={-1},ch[maxn][26],len[maxn],last,sz;
void extend(int c,int id){
	int cur=++sz,p=last,q; len[last=cur]=len[p]+1,ins(rt[cur],1,m,id);
	for(;~p&&!ch[p][c];p=fa[p]) ch[p][c]=cur;
	if(p==-1) fa[cur]=0;
	else if(len[q=ch[p][c]]==len[p]+1) fa[cur]=q;
	else{
		int clone=++sz; len[clone]=len[p]+1,memcpy(ch[clone],ch[q],sizeof ch[q]);
		fa[clone]=fa[q],fa[cur]=fa[q]=clone;
		for(;~p&&ch[p][c]==q;p=fa[p]) ch[p][c]=clone;
	}
}
int pos[maxn*5],pL[maxn*5],F[17][maxn];
vector<int>G[maxn];
void dfs(int u){for(int i=G[u].size()-1,v;i>=0;i--) dfs(v=G[u][i]),F[0][v]=u,rt[u]=merge(rt[u],rt[v],1,m);}
int main()
{
	scanf("%s%d",s+1,&m);
	for(int i=1;i<=m;i++){
		scanf("%s",t+1),last=0;
		for(int j=1;t[j];j++) extend(t[j]-'a',i);
	}
	for(int i=1;i<=sz;i++) G[fa[i]].push_back(i);
	dfs(0);
	for(int j=1;j<=16;j++) for(int i=1;i<=sz;i++) F[j][i]=F[j-1][F[j-1][i]];
	for(int i=1,x=0,Len=0,c;s[i];i++){
		c=s[i]-'a'; while(x&&!ch[x][c]) Len=len[x=fa[x]];
		if(ch[x][c]) x=ch[x][c],Len++;
		pos[i]=x,pL[i]=Len;
	}
	read(Q);
	for(int l,r,L,R,Len;Q--;){
		read(l),read(r),read(L),read(R);
		if((Len=R-L+1)>pL[R]) {printf("%d 0\n",l);continue;}
		int x=pos[R];
		for(int i=16;i>=0;i--) if(len[F[i][x]]>=Len) x=F[i][x];
		node ret=qry(rt[x],1,m,l,r);
		printf("%d %d\n",ret.id,ret.sum);
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip 【备注】 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用!有问题请及时沟通交流。 2、适用人群:计算机相关专业(如计科、信息安全、数据科学与大数据技术、人工智能、通信、物联网、自动化、电子信息等)在校学生、专业老师或者企业员工下载使用。 3、用途:项目具有较高的学习借鉴价值,不仅适用于小白学习入门进阶。也可作为毕设项目、课程设计、大作业、初期项目立项演示等。 4、如果基础还行,或热爱钻研,亦可在此项目代码基础上进行修改添加,实现其他不同功能。 欢迎下载!欢迎交流学习!不清楚的可以私信问我! 毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值