AC自动机模版

刚学习一发AC自动机。

记录一下模版。

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 using namespace std;
  5 const  int maxn = 1000000+50;
  6 struct node{
  7     int Next[26];//每一个节点可以扩展到的字母
  8     int fail; //每一个结点的失配指针
  9     int count;//记录每一个可以构成单词的字符串
 10     void init()//构造 
 11     {
 12         memset(Next,-1,sizeof(Next));//next初始化为-1,代表不连接任何值
 13         fail = 0; //失配指针为空
 14         count = 0;//一开始没有单词,为0. 
 15     } 
 16 }s[maxn];
 17 
 18 int sind;//记录结点的编号
 19 char str[55];//模版串,“单词”、
 20 char des[maxn];//“文章”
 21 int q[maxn],qin,qout;//队列
 22 
 23 void cas_init()//在整个程序前构造root
 24 {
 25     s[0].init();//初始化头结点
 26     sind = 1; //当前有一个结点 
 27 } 
 28 
 29 void ins()//向树中插入字母
 30 {
 31     int len = strlen(str);
 32     int i, j, ind;
 33     for(i = ind = 0;i < len;i++)
 34     {
 35         j = str[i]-'a';//求出字母在next中的编号
 36         if(s[ind].Next[j]== -1)//如果不存在子节点j,则构造新的 
 37         {
 38             s[sind].init();//初始化 
 39             s[ind].Next[j] = sind++;//连向当前结点,并sind++代表总结点数 
 40         }
 41         ind = s[ind].Next[j]; //向下走 
 42     }
 43     s[ind].count++;//增加离根结点这条路径上字符串的个数,一条路上可能不止一个单词 
 44 }
 45 
 46  void make_fail()//构造失配指针
 47  {
 48      qin = qout = 0;//初始化队列
 49      int i,ind,ind_f;
 50      for(i=0;i<26;i++)
 51      {
 52          if(s[0].Next[i]!=-1)
 53              q[qin++]=s[0].Next[i];//先考虑根结点,和根结点相连的都入队 
 54      } 
 55        while(qin!=qout)
 56        {
 57            ind = q[qout++];//记录队首结点
 58         for(i = 0;i < 26; i++)//遍历队首结点的Next 
 59         {
 60             if(s[ind].Next[i]!=-1)//如果结点Next不为空 
 61             {
 62                 q[qin++] = s[ind].Next[i];//将儿子节点入队 
 63                 ind_f = s[ind].fail;//记录节点的失配指针指向 
 64                 while(ind_f>0 && s[ind_f].Next[i]==-1)
 65                 /*当失配指针不为root时,一直循环直到找到一个结点是儿子是i值或者到了root*/ 
 66                     ind_f = s[ind_f].fail;
 67                 if(s[ind_f].Next[i]!=-1) //如果当前结点有儿子的话记录下来备用 
 68                     ind_f = s[ind_f].Next[i];
 69                 s[s[ind].Next[i]].fail = ind_f; //使当前结点的失配指针指向刚才记录的结点完成失配指针的寻找构造 
 70             }
 71         }    
 72        }
 73  }
 74  
 75  int fd()
 76  {
 77      int ct = 0; //记录“单词的个数” 
 78      int di,i,ind ,p; //di为指向“文章”的指针,ind为指向失配结点的指针(即trie树中失配的指针) 
 79      int len = strlen(des);//文章的长度 
 80      for(di = ind =0; di < len ; di++)
 81      {
 82          i = des[di] - 'a';
 83          while(ind>0 && s[ind].Next[i] == -1)
 84      /*当ind指针不是root和找不到结点的儿子是i时一直找下去(类似于KMP中while循环)*/ 
 85              ind = s[ind].fail; //一直寻找失配指针 
 86          
 87          if(s[ind].Next[i]!= -1)//找到了合适的失配指针 
 88          {
 89              ind = s[ind].Next[i]; //指向这个儿子的节点,更新ind的值进行下一次匹配 
 90              
 91              p = ind; //用p来临时代替ind 
 92              while(p>0 && s[p].count!=-1)
 93              /* p>0 表示还没到root,count!=-1表示指针前还有单词*/ 
 94              {
 95                  ct+=s[p].count;//加上有的单词的个数 
 96                  s[p].count = -1;//不重复计算,注意这里很重要 
 97                  p = s[p].fail;//一直寻找失配指针 
 98              }
 99          }
100      }
101      return ct;//返回单词个数 
102  }
103  
104  int main()
105  {
106      int cas,n;
107      scanf("%d",&cas);
108      while(cas-- && scanf("%d",&n))
109      {
110          gets(str);
111          cas_init();//初始化trie树
112         while(n-- && gets(str))
113             ins();//构造trie树
114         make_fail(); //构造失配指针 
115         gets(des);
116         printf("%d\n",fd());  
117      }
118      return 0;
119  }

 

posted on 2015-07-21 10:53  小松song  阅读(144)  评论(0)    收藏  举报

导航