把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【BZOJ2780】Sevenk Love Oimaster(广义后缀自动机)

点此看题面

大致题意: 给定\(n\)个模式串,每次询问一个串是多少模式串的子串。

关于\(AC\)自动机

\(n\)个模式串?多模匹配问题,显然要上\(AC\)自动机嘛!

实际上这题也的确可以用\(AC\)自动机做。不过,由于是在复习后缀自动机,还是决定用后缀自动机写一写这道题。

广义后缀自动机

考虑我们对这\(n\)个串建后缀自动机,然后对于每一个串,枚举其节点暴力往上跳,给沿途所有点打标记,遇到打过当前串标记的节点就退掉,由于每个串每个点只会被打一次标记,复杂度是正确的。

询问就是询问串在后缀自动机上对应节点被打标记的次数。

发现自己写法居然和bzt惊人相似,好荣幸啊!

代码

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 10000
#define LEN 100000
#define MAX 360000
using namespace std;
int n,l[N+5];char s[LEN+5],ss[MAX+5];
class SuffixAutomation
{
	private:
		int Nt,lst;struct node {int C,V,L,F,S[30];}O[LEN<<1];
	public:
		I SuffixAutomation() {Nt=1;}I void Init() {lst=1;}//广义后缀自动机
		I void Ins(CI x,CI ti)//插入新字符
		{
			RI p=lst,now=lst=++Nt;O[now].L=O[p].L+1;
			W(p&&!O[p].S[x]) O[p].S[x]=now,p=O[p].F;if(!p) return (void)(O[now].F=1);
			RI q=O[p].S[x];if(O[p].L+1==O[q].L) return (void)(O[now].F=q);
			RI k=++Nt;(O[k]=O[q]).L=O[p].L+1,O[now].F=O[q].F=k;
			W(p&&O[p].S[x]==q) O[p].S[x]=k,p=O[p].F;
		}
		I void Work(char *s,CI ti)//统计信息
		{
			RI i,p=1,x;for(i=1;i<=l[ti];++i)//枚举点
				{x=p=O[p].S[s[i]&31];W(x&&O[x].V^ti) O[x].V=ti,++O[x].C,x=O[x].F;}//暴力上跳打标记
		}
		I void Solve(char *s)//求解答案
		{
			RI p=1;for(RI i=1,l=strlen(s+1);p&&i<=l;++i) p=O[p].S[s[i]&31];//找到对应点
			printf("%d\n",O[p].C);//返回打标记次数
		}
}S;
int main()
{
	RI Qt,i,j,tot=0;for(scanf("%d%d",&n,&Qt),i=1;i<=n;++i)//枚举模式串
	{
		S.Init(),scanf("%s",s+tot+1),l[i]=strlen(s+tot+1);
		for(j=1;j<=l[i];++j) S.Ins(s[tot+j]&31,i);tot+=l[i];//建后缀自动机
	}
	for(tot=0,i=1;i<=n;++i) S.Work(s+tot,i),tot+=l[i];//维护好信息
	W(Qt--) scanf("%s",ss+1),S.Solve(ss);return 0;//处理询问
}
posted @ 2020-05-28 21:15  TheLostWeak  阅读(193)  评论(1编辑  收藏  举报