CF66E Forensic Examination 题解

继续考虑建立后缀自动机,那么我们查询答案相当于找到最小的包含\(s[l,r]\)的节点,然后求出这个节点子树中\([T_l,T_r]\)出现的次数的最大值以及对应的下标,我们可以找到每一个\(s[1,r]\)的位置,然后倍增,如果说你像我一样弱,不会在广义\(SAM\)中找匹配,你可以把\(s\)也丢进广义\(SAM\),这样一定有匹配,可以直接往儿子节点跳,容易发现此时一定有对应的儿子节点给你跳,代码容易实现,但是时空均比较劣。

代码:

#include <bits/stdc++.h>
using namespace std;
const int maxn=6e5+10;
string s,tt;
int lst,tot=1,n,m,x,y,xx,yy,pre[maxn],idd,now;
struct edge{
	int ch[26];
	int fa[21];
	int link;
	int len;
	int rt;
}tree[maxn<<1];
struct node{
	int ying;
	int mx;
	int ls;
	int rs;
}t[maxn<<5];
vector<int>tu[maxn<<1];
void insert(int c){
	if(tree[lst].ch[c]&&tree[lst].len+1==tree[tree[lst].ch[c]].len){
		lst=tree[lst].ch[c];
		return;
	}
	int p=lst,np,q,nq;
	tot++;
	np=tot;
	tree[np].len=tree[p].len+1;
	for(;p&&!tree[p].ch[c];p=tree[p].link){
		tree[p].ch[c]=np;
	}
	if(!p){
		tree[np].link=1;
	}
	else{
		q=tree[p].ch[c];
		if(tree[p].len+1==tree[q].len){
			tree[np].link=q;
		}
		else{
			tot++;
			nq=tot;
			tree[nq]=tree[q];
			tree[nq].rt=0;
			tree[nq].len=tree[p].len+1;
			tree[q].link=nq;
			tree[np].link=nq;
			for(;p&&tree[p].ch[c]==q;p=tree[p].link){
				tree[p].ch[c]=nq;
			}
		}
	}
	lst=np;
	return;
}
void push_up(int id){
	if(t[t[id].ls].mx>=t[t[id].rs].mx){
		t[id].mx=t[t[id].ls].mx;
		t[id].ying=t[t[id].ls].ying;
	}
	else{
		t[id].mx=t[t[id].rs].mx;
		t[id].ying=t[t[id].rs].ying;
	}
	return;
}
void add(int &id,int l,int r,int q){
	if(!id){
		idd++;
		id=idd;
	}
	if(l==r){
		t[id].mx++;
		t[id].ying=l;
		return;
	}
	int mid=(l+r)/2;
	if(q<=mid){
		add(t[id].ls,l,mid,q);
	}
	else{
		add(t[id].rs,mid+1,r,q);
	}
	push_up(id);
	return;
}
int merge(int q,int w){
	if(!q||!w){
		return q+w;
	}
	idd++;
	int zhi=idd;
	if(!t[q].ls&&!t[q].rs){
		t[zhi].mx=t[q].mx+t[w].mx;
		t[zhi].ying=t[q].ying;
		return zhi;
	}
	t[zhi].ls=merge(t[q].ls,t[w].ls);
	t[zhi].rs=merge(t[q].rs,t[w].rs);
	push_up(zhi);
	return zhi;
}
int query_mx(int id,int l,int r,int q,int w){
	if(q<=l&&r<=w){
		return t[id].mx;
	}
	int mid=(l+r)/2;
	if(w<=mid){
		return query_mx(t[id].ls,l,mid,q,w);
	}
	else if(q>mid){
		return query_mx(t[id].rs,mid+1,r,q,w);
	}
	else{
		return max(query_mx(t[id].ls,l,mid,q,w),query_mx(t[id].rs,mid+1,r,q,w));
	}
}
int query_shu(int id,int l,int r,int q,int w,int qw){
	if(t[id].mx<qw){
		return 0;
	}
	if(l==r){
		return l;
	}
	int now=0,mid=(l+r)/2;
	if(q<=mid){
		now=query_shu(t[id].ls,l,mid,q,w,qw);
	}
	if(w>mid&&!now){
		now=query_shu(t[id].rs,mid+1,r,q,w,qw);
	}
	return now;
}
void query(int id,int l,int r){
	int now=query_mx(tree[id].rt,1,n,l,r);
	cout<<query_shu(tree[id].rt,1,n,l,r,now)<<' '<<now<<'\n';
	return;
}
void dfss(int q){
	for(int i=1;i<=20;i++){
		tree[q].fa[i]=tree[tree[q].fa[i-1]].fa[i-1];
	}
	for(int i=(int)tu[q].size()-1;i>=0;i--){
		dfss(tu[q][i]);
		tree[q].rt=merge(tree[q].rt,tree[tu[q][i]].rt);
	}
	return;
}
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>s;
	lst=1;
	for(int i=0;i<(int)s.size();i++){
		insert(s[i]-'a');
	}
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>tt;
		lst=1;
		for(int j=0;j<(int)tt.size();j++){
			insert(tt[j]-'a');
			add(tree[lst].rt,1,n,i);
		}
	}
	for(int i=2;i<=tot;i++){
		tu[tree[i].link].push_back(i);
		tree[i].fa[0]=tree[i].link;
	}
	dfss(1);
	pre[0]=1;
	for(int i=1;i<=(int)s.size();i++){
		pre[i]=tree[pre[i-1]].ch[s[i-1]-'a'];
	}
	cin>>m;
	for(int i=1;i<=m;i++){
		cin>>x>>y>>xx>>yy;
		now=pre[yy];
		for(int j=20;j>=0;j--){
			if(tree[tree[now].fa[j]].len>=yy-xx+1){
				now=tree[now].fa[j];
			}
		}
		query(now,x,y);
	}
	return 0;
}
posted @ 2025-05-19 14:42  特别之处  阅读(10)  评论(0)    收藏  举报