CF666E Forensic Examination(广义后缀自动机+线段树合并)

CF666E Forensic Examination(广义后缀自动机+线段树合并)

Luogu

给你一个串 $ S $ 以及一个字符串数组 $ T_1 ~ T_m $ , $ q $ 次询问,每次问 $ S $ 的子串 $ S[p_l,p_r] $ 在 $ T_l ~ T_r $ 中的哪个串里的出现次数最多,并输出出现次数。

如有多解输出最靠前的那一个。

题解时间

SAM的毒瘤题,无论是倍增来满足长度限制,线段树合并来求区间询问,应有尽有。。。

对于 $ T $ 串建广义SAM,之后考虑如何使得 $ S $ 在SAM上匹配时求出 $ S $ 在每个 $ T $ 的出现次数。

很明显用线段树合并就可以搞:

每插入 $ T_i $ 的一个字符就在 $ fin $ 上挂上一个 $ i $ 。

然后跳parent树搞线段树合并就好。

之后,由于每次 $ S $ 也只选中一部分,所以考虑:

对于一组询问 $ [l,r,pl,pr] $ ,首先毫无疑问将询问挂在 $ S $ 的 $ pr $ 位上。

之后匹配到 $ pr $ 位时,倍增跳现所处点 $ px $ 的 $ pre $ 到最上面的 $ x $ 使得 $ len[x]>=(pr-pl+1) $ 。

然后线段树上求答案即可。

#include<bits/stdc++.h>
using namespace std;
namespace RKK
{
const int N=1000011;
int n,m,qaq;
char s0[N],s1[N];int l0,l1;
struct sumireko{int to,ne;}e[N];int he[N],ecnt;
void addline(int f,int t){e[++ecnt].to=t;e[ecnt].ne=he[f];he[f]=ecnt;}
int fa[N][22];
int tcnt,rt[N],lson[N<<3],rson[N<<3];
struct pat
{
	int first,second;
	bool operator < (const pat &a)const{return second==a.second?first>a.first:second<a.second;}
};
pat ma[N<<3],ans[N];
void fuckup(int px){ma[px]=max(ma[lson[px]],ma[rson[px]]);}
void insert(int x,int &px,int pl,int pr)
{
	if(!px) px=++tcnt;
	if(pl==pr)
	{
		ma[px].first=x,ma[px].second++;
		return;
	}
	int pm=pl+pr>>1;
	if(x<=pm) insert(x,lson[px],pl,pm);
	else insert(x,rson[px],pm+1,pr);
	fuckup(px);
}
int merge(int px,int py,int pl,int pr)
{
	if(!px||!py) return px|py;
	int pz=++tcnt;
	if(pl==pr){ma[pz]=(pat){pl,ma[px].second+ma[py].second};}
	else
	{
		int pm=pl+pr>>1;
		lson[pz]=merge(lson[px],lson[py],pl,pm);
		rson[pz]=merge(rson[px],rson[py],pm+1,pr);
		fuckup(pz);
	}
	return pz;
}
void query(int l,int r,int px,int pl,int pr,pat &pa)
{
	if(!px) return;
	if(l<=pl&&r>=pr){pa=max(pa,ma[px]);return;}
	int pm=pl+pr>>1;
	if(l<=pm) query(l,r,lson[px],pl,pm,pa);
	if(r>pm) query(l,r,rson[px],pm+1,pr,pa);
}
struct ques{int p,le,l,r,id;};
vector<ques> ve[N];
struct remilia{int tranc[26],len,pre;};
struct sakuya
{
	remilia s[N];
	int fin,size;
	sakuya(){fin=size=1;}
	void ins(int ch)
	{
		int npx,npy,lpx,lpy;
        if(s[fin].tranc[ch])
        {
            lpx=fin,lpy=s[fin].tranc[ch];
            if(s[lpy].len==s[lpx].len+1) fin=lpy;
            else
            {
                npy=++size;
                s[npy]=s[lpy];
                s[npy].len=s[lpx].len+1;
                s[lpy].pre=npy;
                while(s[lpx].tranc[ch]==lpy)
                {
                    s[lpx].tranc[ch]=npy;
                    lpx=s[lpx].pre;
                }
                fin=npy;
            }
            return;
        }
		npx=++size;
		s[npx].len=s[fin].len+1;
		for(lpx=fin;lpx&&!s[lpx].tranc[ch];lpx=s[lpx].pre) s[lpx].tranc[ch]=npx;
		if(!lpx) s[npx].pre=1;
		else
		{
			lpy=s[lpx].tranc[ch];
			if(s[lpy].len==s[lpx].len+1) s[npx].pre=lpy;
			else
			{
				npy=++size;
				s[npy]=s[lpy];
				s[npy].len=s[lpx].len+1;
				s[npx].pre=s[lpy].pre=npy;
				while(s[lpx].tranc[ch]==lpy)
				{
					s[lpx].tranc[ch]=npy;
					lpx=s[lpx].pre;
				}
			}
		}
		fin=npx;
	}
	void dfs(int x)
	{
		for(int i=he[x],t=e[i].to;i;i=e[i].ne,t=e[i].to)
		{
			dfs(t),rt[x]=merge(rt[x],rt[t],1,m);
		}
	}
	void work()
	{
		for(int i=2;i<=size;i++) fa[i][0]=s[i].pre,addline(s[i].pre,i);
		for(int k=1;k<=21;k++)for(int i=2;i<=size;i++) fa[i][k]=fa[fa[i][k-1]][k-1];
		dfs(1);
		int px=1,lnow=0;
		for(int i=1;i<=l0;i++)
		{
			while(px&&!s[px].tranc[s0[i]-'a']) px=s[px].pre,lnow=s[px].len;
			if(!px) px=1,lnow=0;
			else px=s[px].tranc[s0[i]-'a'],lnow++;
			for(int j=0;j<ve[i].size();j++)
			{
				ques &qn=ve[i][j];
				ans[qn.id].first=qn.l;
				if(lnow<qn.le){continue;}
				int x=px;
				for(int k=21;k>=0;k--)if(s[fa[x][k]].len>=qn.le) x=fa[x][k];
				query(qn.l,qn.r,rt[x],1,m,ans[qn.id]);
			}
		}
	}
}sam;
int Iris()
{
	scanf("%s",s0+1),l0=strlen(s0+1);
	scanf("%d",&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%s",s1+1),l1=strlen(s1+1);
		sam.fin=1;
		for(int j=1;j<=l1;j++) sam.ins(s1[j]-'a'),insert(i,rt[sam.fin],1,m);
	}
	scanf("%d",&qaq);
	for(int i=1,l,r,x,y;i<=qaq;i++)
	{
		scanf("%d%d%d%d",&l,&r,&x,&y);
		ve[y].push_back((ques){y,y-x+1,l,r,i});
	}
	sam.work();
	for(int i=1;i<=qaq;i++)
		printf("%d %d
",ans[i].first,ans[i].second);
	return 0;
}
}
int main(){return RKK::Iris();}
原文地址:https://www.cnblogs.com/rikurika/p/12079264.html