bzoj1212: [HNOI2004]L语言(ac自动机)

传送门
思路:
对单词建出 a c ac ac自动机。
然后每次用文章去匹配的时候我们暴力枚举当前点在 f a i l fail fail树上的所有祖先看是否有已经可以匹配的即可。
代码:

#include<bits/stdc++.h>
#define ri register int
using namespace std;
const int rlen=1<<18|1;
inline char gc(){
	static char buf[rlen],*ib,*ob;
	(ib==ob)&&(ob=(ib=buf)+fread(buf,1,rlen,stdin));
	return ib==ob?-1:*ib++;
}
inline int read(){
	int ans=0;
	char ch=gc();
	while(!isdigit(ch))ch=gc();
	while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
	return ans;
}
inline int Read(char*s){
	int top=0;
	char ch=gc();
	while(!isalpha(ch))ch=gc();
	while(isalpha(ch))s[++top]=ch,ch=gc();
	return top;
}
typedef long long ll;
int n,m;
char s[20];
bool ed[210];
char qry[1200005];
namespace acam{
	int son[210][26],fail[210],tot,dep[210];
	inline int newnode(){return fail[++tot]=0,ed[tot]=0,memset(son[tot],0,sizeof(son[tot])),tot;}
	inline void insert(char*s,int up){
		ri p=0;
		for(ri i=1,x;i<=up;++i){
			if(!son[p][x=s[i]-'a'])son[p][x]=newnode();
			dep[son[p][x]]=dep[p]+1;
			p=son[p][x];
		}
		ed[p]=1;
	}
	inline void getfail(){
		static int q[210],hd,tl;
		hd=1,tl=0;
		for(ri i=0;i<26;++i)if(son[0][i])q[++tl]=son[0][i];
		while(hd<=tl){
			int p=q[hd++];
			for(ri i=0;i<26;++i){
				if(!son[p][i]){son[p][i]=son[fail[p]][i];continue;}
				fail[q[++tl]=son[p][i]]=son[fail[p]][i];
			}
		}
	}
	inline void query(char*s,int up){
		static bool ok[1000005];
		int ans=0;
		ok[0]=1;
		for(ri i=1,p=0;i<=up;++i){
			ok[i]=0;
			p=son[p][s[i]-'a'];
			for(ri j=p;j;j=fail[j])if(ed[j]&&ok[i-dep[j]]){ok[i]=1;break;};
		}
		for(ri i=up;~i;--i)if(ok[i]){cout<<i<<'\n';return;}
	}
}
int main(){
	n=read(),m=read();
	for(ri len,i=1;i<=n;++i){
		len=Read(s);
		acam::insert(s,len);
	}
	acam::getfail();
	for(ri i=1,len;i<=m;++i){
		len=Read(qry);
		acam::query(qry,len);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值