【BZOJ4327】JSOI2012 玄武密码
Description
在美丽的玄武湖畔,鸡鸣寺边,鸡笼山前,有一块富饶而秀美的土地,人们唤作进香河。相传一日,一缕紫气从天而至,只一瞬间便消失在了进香河中。老人们说,这是玄武神灵将天书藏匿在此。
很多年后,人们终于在进香河地区发现了带有玄武密码的文字。更加神奇的是,这份带有玄武密码的文字,与玄武湖南岸台城的结构有微妙的关联。于是,漫长的破译工作开始了。
经过分析,我们可以用东南西北四个方向来描述台城城砖的摆放,不妨用一个长度为N的序列来描述,序列中的元素分别是‘E’,‘S’,‘W’,‘N’,代表了东南西北四向,我们称之为母串。而神秘的玄武密码是由四象的图案描述而成的M段文字。这里的四象,分别是东之青龙,西之白虎,南之朱雀,北之玄武,对东南西北四向相对应。
现在,考古工作者遇到了一个难题。对于每一段文字,其前缀在母串上的最大匹配长度是多少呢?
Input
第一行有两个整数,N和M,分别表示母串的长度和文字段的个数。
第二行是一个长度为N的字符串,所有字符都满足是E,S,W和N中的一个。
之后M行,每行有一个字符串,描述了一段带有玄武密码的文字。依然满足,所有字符都满足是E,S,W和N中的一个。
Output
输出有M行,对应M段文字。
每一行输出一个数,表示这一段文字的前缀与母串的最大匹配串长度。
Sample Input
7 3
SNNSSNS
NNSS
NNN
WSEE
SNNSSNS
NNSS
NNN
WSEE
Sample Output
4
2
0
2
0
HINT
对于100%的数据,N<=10^7,M<=10^5,每一段文字的长度<=100。
题解:沿着AC自动机搜几遍即可。
#include <cstdio> #include <cstring> #include <iostream> #include <queue> using namespace std; const int maxn=10000010; struct node { int ch[4],fail,cnt,vis,ans,dep; }p[maxn]; int dic(char le) { switch(le) { case 'E':return 0; case 'S':return 1; case 'W':return 2; case 'N':return 3; } } int n,m,tot; int Q[maxn],pos[100010]; char w[110],str[maxn]; queue<int> q; void build() { int i,u,t; q.push(1); while(!q.empty()) { u=q.front(),q.pop(); Q[++Q[0]]=u; for(i=0;i<4;i++) { if(p[u].ch[i]) { q.push(p[u].ch[i]); t=p[u].fail; while(!p[t].ch[i]&&t) t=p[t].fail; if(t) p[p[u].ch[i]].fail=p[t].ch[i]; else p[p[u].ch[i]].fail=1; } } } } void search() { int i,j,u=1,t; p[u].vis=0; for(i=0;i<n;i++) { while(!p[u].ch[dic(str[i])]&&u) u=p[u].fail; u=p[u].ch[dic(str[i])]; u=u>0?u:1; p[u].vis=1; } for(i=tot;i>=2;i--) p[p[Q[i]].fail].vis|=p[Q[i]].vis; for(i=1;i<=tot;i++) { if(p[Q[i]].vis) p[Q[i]].ans=p[Q[i]].dep; for(j=0;j<4;j++) if(p[Q[i]].ch[j]) p[p[Q[i]].ch[j]].ans=p[Q[i]].ans; } for(i=1;i<=m;i++) printf("%d ",p[pos[i]].ans); } int main() { scanf("%d%d%s",&n,&m,str); int i,j,k,u,t; tot=1; for(i=1;i<=m;i++) { scanf("%s",w); k=strlen(w); u=1; for(j=0;j<k;j++) { if(!p[u].ch[dic(w[j])]) p[u].ch[dic(w[j])]=++tot; u=p[u].ch[dic(w[j])]; p[u].dep=j+1; } p[u].cnt++; pos[i]=u; } build(); search(); return 0; }