【BZOJ 3172】[Tjoi2013]单词 AC自动机

关于AC自动机:一个在kmp与Trie的基础上建立的数据结构,关键在于Trie树结构与fail指针,他们各有各的应用。在AC自动机里最典型的就是多串匹配,原本效率为O(n*l+n*l+m*l),(n是模式串个数,m是匹配串长度,l是模式串平均长度),那么他的效率甚至有时不如多个kmp,虽然很好被卡但平均效率还是不错的。如果在此基础上加上last指针,作用为减少匹配过程中不必要的跳fail,那么就会好一些,但是效率不会有显著的提升而且仍然很好卡,目前为O(n*l+n*l+m*n)。在此基础上我们就可以加上Trie图,Trie图作用就是加快建fail以及在匹配过程中的转移,这时效率就会有不错的提升,建fail的时间复杂度也就更有保证,但是仍然会被卡,目前为O(n*l+n*l+m*n)。到此为止我们的时间复杂度已经是比较优秀的了,但是我们还可以让他更加优秀,我们可以在加上fail树,关于fail树,那么我们在匹配过程中就可以省去跳跃的过程,现在我们的时间复杂度已经到了O(n*l+n*l+m+n*l),已经有了巨大的飞跃,而且基本不会被卡。

我的程序是倒数第二层优化的,慢死。

#include <vector>
#include <cstring>
#include <cstdio>
#include <iostream>
const int N=1000000;
char s[201][N];
struct Trie{
  Trie *ch[26],*fail,*last;
  std::vector<int> mem;
}node[N+1],*root,*q[N+1];
int n,sz,sum[201];
inline Trie *newnode(){
  return &node[++sz];
}
inline void insert(char *w,int id){
  Trie *p=root;
  for(int i=0;w[i];i++){
    if(p->ch[w[i]-'a']==NULL)p->ch[w[i]-'a']=newnode();
    p=p->ch[w[i]-'a'];
  }
  p->mem.push_back(id);
}
inline void build(){
  q[0]=root;
  for(int i=0,j=0;i<=j;i++)
    for(int l=0;l<26;l++)
      if(q[i]->ch[l]){
        q[++j]=q[i]->ch[l];
        q[j]->fail=q[i]==root?root:q[i]->fail->ch[l];
        q[j]->last=q[j]->fail->mem.size()?q[j]->fail:q[j]->fail->last;
      }
      else
        q[i]->ch[l]=q[i]==root?root:q[i]->fail->ch[l];
}
inline void read(){
  scanf("%d",&n),root=node;
  for(int i=1;i<=n;i++)
    scanf("%s",s[i]),insert(s[i],i);
  build();
}
inline void work(){
  for(int k=1;k<=n;k++){
    Trie *now=root;
    for(int i=0;s[k][i];i++){
      now=now->ch[s[k][i]-'a'];
      for(int j=0;j<now->mem.size();j++)
        sum[now->mem[j]]++;
      for(Trie *p=now->last;p;p=p->last)
        for(int j=0;j<p->mem.size();j++)
          sum[p->mem[j]]++;
    }
  }
}
inline void print(){
  for(int i=1;i<=n;i++)
    printf("%d\n",sum[i]);
}
int main(){
  read();
  work();
  print();
}

 

posted @ 2017-09-13 21:16  TS_Hugh  阅读(136)  评论(0编辑  收藏  举报