BZOJ4327 : JSOI2012 玄武密码
对所有询问串建立AC自动机。
然后将母串在AC自动机上跑,每走到一个点x,从x点出发沿着fail指针能到的所有前缀都是匹配成功的,暴力向上走,碰到走过的就break,这样每个点最多只会被标记一次。
时间复杂度$O(N+100M)$。
#include<cstdio> #include<cstring> const int N=10000003,M=100010; int tot,son[N][4],f[N],fail[N],q[N];bool v[N]; int n,m,i,fin[M],len[M];char a[N],b[103]; inline int id(char x){ if(x=='S')return 0; if(x=='E')return 1; if(x=='W')return 2; return 3; } inline void insert(int p){ int l=len[p]=strlen(b),x=0,i=0,w; for(;i<l;x=son[x][w],i++)if(!son[x][w=id(b[i])])f[son[x][w]=++tot]=x; fin[p]=x; } void make(){ int h=1,t=0,i,x;fail[0]=-1; for(i=0;i<4;i++)if(son[0][i])q[++t]=son[0][i]; while(h<=t)for(x=q[h++],i=0;i<4;i++)if(son[x][i])fail[q[++t]=son[x][i]]=son[fail[x]][i];else son[x][i]=son[fail[x]][i]; } void solve(){ for(int x=0,i=0,w;i<n;i++){ x=son[x][w=id(a[i])]; for(int y=x;~y;y=fail[y])if(v[y])break;else v[y]=1; } } inline int ask(int p){ for(int x=fin[p],y=len[p];y;y--,x=f[x])if(v[x])return y; return 0; } int main(){ scanf("%d%d%s",&n,&m,a); for(i=0;i<m;i++)scanf("%s",b),insert(i); make(); solve(); for(i=0;i<m;i++)printf("%d\n",ask(i)); return 0; }