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
",ask(i));
  return 0;
}

  

原文地址:https://www.cnblogs.com/clrs97/p/4982823.html