Loading

题解 [JSOI2012]玄武密码

极简思路:

先跑一边AC自动机,处理出 fail 数组,然后再把文本串匹配一下,可以获得 vis 数组,代表着 trie 树上的这个点是文本串的前缀。最后只需要再每个模式串跑一遍 trie 树,就可以得到最长的公共前缀长度了。

#include<bits/stdc++.h>
using namespace std;
const int N=1e7+5,M=1e5+5;
int n,lent;
int trie[N][26],tot,fail[N],End[N];
int vis[N];
char tmp[M][105],t[N];
queue<int> q;
void ins(char *str){
  int len=strlen(str),p=0;
  for(int i=0;i<len;i++){
    int ch=str[i]-'A';
    if(!trie[p][ch]) trie[p][ch]=++tot;
    p=trie[p][ch];
  }
  End[p]++;
}
void build(){
  for(int i=0;i<26;i++) if(trie[0][i]) q.push(trie[0][i]),fail[trie[0][i]]=0;
  while(!q.empty()){
    int u=q.front();q.pop();
    for(int i=0;i<26;i++){
      if(trie[u][i]) fail[trie[u][i]]=trie[fail[u]][i],q.push(trie[u][i]);
      else trie[u][i]=trie[fail[u]][i];
    }
  }
  int p=0;
  for(int i=0;i<lent;i++){
    p=trie[p][t[i]-'A'];
    for(int k=p;k&&!vis[k];k=fail[k]) vis[k]=1;
  }
}
int Query(char *str){
  int len=strlen(str),p=0,res=0;
  for(int i=0;i<len;i++){
    p=trie[p][str[i]-'A'];
    if(vis[p]) res=i+1;
  }
  return res;
}
int main(){
  scanf("%d%d",&lent,&n);
  scanf("%s",t);
  for(int i=1;i<=n;i++){
    scanf("%s",tmp[i]);
    ins(tmp[i]);
  }
  build();
  for(int i=1;i<=n;i++){
    printf("%d\n",Query(tmp[i]));
  }
  return 0;
}
posted @ 2021-02-08 10:21  Quick_Kk  阅读(72)  评论(1编辑  收藏  举报