【CF666E】Forensic Examination(后缀自动机,线段树合并)

【CF666E】Forensic Examination(后缀自动机,线段树合并)

题面

洛谷
CF

翻译:
给定一个串\(S\)和若干个串\(T_i\)
每次询问\(S[pl..pr]\)\(T_l..T_r\)中出现的最多次数,以及出现次数最多的那个串的编号。

题解

好题啊。

我们对于所有的\(T\)串构建出广义后缀自动机之后
\(S\)丢到\(SAM\)上匹配,对于每组询问,
相当于回答\(S[pl..pr]\)所代表的节点的\(right\)集合所代表的串的众数是哪个串,以及这个众数出现的次数。
考虑如何维护\(right\)集合关于每个\(T\)的出现次数

我们对于每个节点开一棵线段树,线段树的值域是\(1..m\),用来维护每个\(T\)串的出现次数。
每个节点维护的值就是\(T\)串的最多出现次数。
这样就挺好做了,\(right\)集合只需要线段树合并就可以快速求得。
对于每个询问挂链,然后\(dfs\)一遍\(parent\)树的时候,顺便在线段树上查询一下区间最大就行了。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define RG register
#define MAX 1111111
inline int read()
{
    RG int x=0,t=1;RG char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=-1,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return x*t;
}
struct SAM
{
	struct Node
	{
		int son[26];
		int ff,len;
	}t[MAX];
	int last,tot;
	void init(){last=tot=1;}
	void extend(int c)
	{
		int p=last,np=++tot;last=np;
		t[np].len=t[p].len+1;
		while(p&&!t[p].son[c])t[p].son[c]=np,p=t[p].ff;
		if(!p)t[np].ff=1;
		else
		{
			int q=t[p].son[c];
			if(t[q].len==t[p].len+1)t[np].ff=q;
			else
			{
				int nq=++tot;
				t[nq]=t[q];
				t[nq].len=t[p].len+1;
				t[q].ff=t[np].ff=nq;
				while(p&&t[p].son[c]==q)t[p].son[c]=nq,p=t[p].ff;
			}
		}
	}
}SAM;

int n,m,f[22][MAX];
struct Data{int v,p;}ans[MAX];
bool operator<(Data a,Data b){return (a.v<b.v)||(a.v==b.v&&a.p>b.p);}
struct SegmentTree{int ls,rs;Data v;}t[MAX<<4];
int Tot,rt[MAX];
struct query{int l,r,pl,pr;}q[MAX];
char S[MAX],T[MAX];
void Modify(int &x,int l,int r,int p)
{
	if(!x)x=++Tot;
	if(l==r){t[x].v.v++;t[x].v.p=p;return;}
	int mid=(l+r)>>1;
	if(p<=mid)Modify(t[x].ls,l,mid,p);
	else Modify(t[x].rs,mid+1,r,p);
	t[x].v=max(t[t[x].ls].v,t[t[x].rs].v);
}
void Merge(int &x,int y)
{
	if(!x||!y){x|=y;return;}
	if(!t[x].ls&&!t[x].rs){t[x].v.v+=t[y].v.v;return;}
	Merge(t[x].ls,t[y].ls);Merge(t[x].rs,t[y].rs);
	t[x].v=max(t[t[x].ls].v,t[t[x].rs].v);
}
Data Query(int x,int l,int r,int L,int R)
{
	if(L==l&&r==R)return t[x].v;
	int mid=(l+r)>>1;
	if(R<=mid)return Query(t[x].ls,l,mid,L,R);
	if(L>mid)return Query(t[x].rs,mid+1,r,L,R);
	return max(Query(t[x].ls,l,mid,L,mid),Query(t[x].rs,mid+1,r,mid+1,R));
}

struct Link
{
	struct Line{int v,next;}e[MAX];
	int h[MAX],cnt;
	void Add(int u,int v){e[++cnt]=(Line){v,h[u]};h[u]=cnt;}
}Par,Qy,Aw;

void dfs(int u)
{
	for(int i=Par.h[u];i;i=Par.e[i].next)
		dfs(Par.e[i].v),Merge(rt[u],rt[Par.e[i].v]);
	for(int i=Aw.h[u];i;i=Aw.e[i].next)
		ans[Aw.e[i].v]=Query(rt[u],1,m,q[Aw.e[i].v].l,q[Aw.e[i].v].r);
}

int main()
{
	scanf("%s",S+1);n=strlen(S+1);
	m=read();SAM.init();
	for(int i=1;i<=m;++i)
	{
		SAM.last=1;scanf("%s",T+1);
		for(int j=1,l=strlen(T+1);j<=l;++j)
			SAM.extend(T[j]-97),Modify(rt[SAM.last],1,m,i);
	}
	int Q=read();
	for(int i=1;i<=Q;++i)
	{
		q[i]=(query){read(),read(),read(),read()};
		Qy.Add(q[i].pr,i);
	}
	for(int i=2;i<=SAM.tot;++i)Par.Add(f[0][i]=SAM.t[i].ff,i);
	for(int i=1;i<22;++i)
		for(int j=1;j<=SAM.tot;++j)f[i][j]=f[i-1][f[i-1][j]];
	for(int i=1,nw=1,len=0;i<=n;++i)
	{
		int c=S[i]-97;
		while(nw&&!SAM.t[nw].son[c])nw=SAM.t[nw].ff,len=SAM.t[nw].len;
		if(!nw){nw=1,len=0;continue;}
		nw=SAM.t[nw].son[c];len+=1;
		for(int j=Qy.h[i];j;j=Qy.e[j].next)
		{
			int v=Qy.e[j].v,u=nw;
			if(len<q[v].pr-q[v].pl+1)continue;
			for(int k=21;~k;--k)
				if(SAM.t[f[k][u]].len>=q[v].pr-q[v].pl+1)u=f[k][u];
			Aw.Add(u,v);
		}
	}
	dfs(1);
	for(int i=1;i<=Q;++i)
	{
		if(!ans[i].v)ans[i].p=q[i].l;
		printf("%d %d\n",ans[i].p,ans[i].v);
	}
	return 0;
}

posted @ 2018-04-12 09:17  小蒟蒻yyb  阅读(1539)  评论(0编辑  收藏  举报