zoukankan      html  css  js  c++  java
  • AC自动机

      1 #include<iostream>
      2 #include<algorithm>
      3 #include<queue>
      4 #include<string.h>
      5 #include<stdlib.h>
      6 
      7 using namespace std;
      8 
      9 #define MAX_N 1000006
     10 #define MAX_Tot 500005
     11 
     12 struct ACo{
     13     struct state{
     14         //子節點數組 
     15         int next[26];
     16         //當前節點的失敗指針 
     17         int fail;
     18         //到當前位置的字符串結束個數 
     19         int cnt;
     20         
     21     }stateTable[MAX_Tot];
     22     
     23     //當前AC自動機樹的節點個數 
     24     int size; 
     25     queue<int> que;
     26     //初始化 
     27     void init()
     28     {
     29         //將節點初始化 
     30         while(que.size()) que.pop();
     31         for(int i=0;i<MAX_Tot;i++)
     32         {
     33             memset(stateTable[i].next,0,sizeof(stateTable[i].next));
     34             stateTable[i].fail = 0;
     35             stateTable[i].cnt = 0; 
     36         }
     37         //根節點一定存在,所以節點個數爲1 
     38         size = 1;
     39     } 
     40     
     41     //構建字典樹 
     42     void insert(char *str)
     43     {
     44         int n = strlen(str);
     45         int now = 0;
     46         for(int i=0;i<n;i++)
     47         {
     48             char c = str[i];
     49             //如果到當前節點子節點不存在的話 
     50             if(!stateTable[now].next[c-'a'])
     51             {
     52                 //開闢新節點,並將節點個數加一,注意size從1開始的 
     53                 stateTable[now].next[c-'a'] = size++;
     54             }    
     55             //每次都要進行走到下一節點 
     56             now = stateTable[now].next[c-'a'];
     57             
     58         } 
     59         //該字符串便利完之後走到的節點 
     60         stateTable[now].cnt++;
     61     } 
     62     //構造失配指針 
     63     void build()
     64     {
     65         
     66         //根節點的失配指針設爲-1 
     67         stateTable[0].fail = -1;
     68         //將根節點壓入隊列 
     69         que.push(0);
     70         
     71         while(que.size())
     72         {
     73             //取當前隊列中的第一個,即廣度優先遍歷數,保證每層節點的失敗指針都選擇完成,纔有可能繼續下一層
     74             //否則,如果深度優先遍歷會導致指針不爲最優,因爲別的叉沒有被構造。 
     75             int u = que.front();
     76             //取一個,要將其彈出 
     77             que.pop();
     78             
     79             //將當前節點的所有子節點遍歷 
     80             for(int i=0;i<26;i++)
     81             {
     82                 //如果當前節點的子節點之一存在子節點 
     83                 if(stateTable[u].next[i])
     84                 {
     85                     //判斷當前點是否爲根節點 
     86                     if(u==0)
     87                     {
     88                         //如果爲根節點,沒辦法,只能讓當前節點的子節點的失敗指針指向0,即指向根節點 
     89                         //根節點的第一層節點都滿足,可以想一下。 
     90                         stateTable[stateTable[u].next[i]].fail = 0;
     91                     }
     92                     //否則 
     93                     else
     94                     {
     95                         //記錄當前節點的失敗指針 
     96                         int v = stateTable[u].fail;
     97                         //如果失敗指針存在的話 
     98                         while(v!=-1)
     99                         {
    100                             //並且其失敗指針節點也存在子節點 
    101                             if(stateTable[v].next[i])
    102                             {
    103                                 //將當前節點 的 失敗指針 指向其 失敗指針節點 的下一個節點 
    104                                 stateTable[stateTable[u].next[i]].fail = stateTable[v].next[i] ;
    105                                 break;
    106                             }
    107                             //記錄下失敗指針的位置。 
    108                             v = stateTable[v].fail; 
    109                         }
    110                         //如果找了半天,其各種祖先節點的失敗指針仍然不存在 
    111                         if(v==-1)
    112                         {
    113                             //只能將當前節點的失敗指針指向根節點 
    114                             stateTable[stateTable[u].next[i]].fail = 0;
    115                         }
    116                     }
    117                     //將當前節點入隊列,畢竟要廣搜一層來確定 
    118                     que.push(stateTable[u].next[i]);
    119                 }
    120             }
    121         }
    122         
    123     }
    124     int Get(int u)
    125     {
    126         int res = 0;
    127         while(u)
    128         {
    129             //當前節點的不爲根節點
    130             //res+上當前節點下的單詞數 
    131             res = res+stateTable[u].cnt;
    132             //當前節點單詞數清零,避免一條子樹下重複加值 
    133             stateTable[u].cnt = 0;
    134             //回溯其失敗指針下滿足條件的單詞數 
    135             u = stateTable[u].fail;
    136         }
    137         return res;
    138     }
    139     //製造匹配函數 
    140     int match(char *S)
    141     {
    142         int n = strlen(S);
    143         int res = 0,now = 0;
    144         for(int i=0;i<n;i++)
    145         {
    146             char c = S[i];
    147             //存在自不必多說,向下一個指針移動 
    148             if(stateTable[now].next[c-'a'])
    149             {
    150                 now = stateTable[now].next[c-'a']; 
    151             }
    152             else
    153             {
    154                 //一旦失配,不回溯根節點,而是回溯失敗指針節點 
    155                 int p = stateTable[now].fail;
    156                 //如果失配指針存在,或者失配指針指向的根節點存在 
    157                 while(p!=-1 && stateTable[p].next[c-'a']==0)
    158                 {
    159                     //更新失敗指針的位置,即不=停的向父節點靠攏 
    160                     p = stateTable[p].fail;
    161                 }
    162                 //如果只能找到根節點,那就從根節點進行匹配 
    163                 if(p==-1)
    164                 {
    165                     now = 0;
    166                 }
    167                 else{
    168                     //不然,當前節點跳到失敗節點的存在子節點 
    169                     now = stateTable[p].next[c-'a'];
    170                 }
    171             }
    172             //如果當前節點下存在的單詞數不爲0 
    173             if(stateTable[now].cnt)
    174             {
    175                 //記錄到當前節點爲止所有含有的父節點(失敗節點)的單詞數, 
    176                 res +=Get(now);
    177             }
    178         }
    179         return res;
    180     }
    181 }aho;
    182 int T;
    183 int n;
    184 char S[MAX_N];
    185 
    186 int main()
    187 {
    188     scanf("%d",&T);
    189     while(T--)
    190     {
    191         aho.init();
    192         scanf("%d",&n);
    193         for(int i=0;i<n;i++)
    194         {
    195             scanf("%s",S);
    196             aho.insert(S);
    197         }
    198         aho.build();
    199         scanf("%s",S);
    200         printf("%d
    ",aho.match(S));
    201     }
    202  } 
  • 相关阅读:
    CVS简介
    快捷搭建JavaWeb开发环境
    局域网Win7 SVN 服务器搭建以及使用(原创)
    给自己 一个方向,让自己不再迷茫
    SQL Server2005安装配置以及测试
    Oracle中表结构和表内容复制
    Myeclipse8.5 svn插件安装两种方式
    Oracle中错误代码ORA02292 违反了完整性约束条件解决
    JSP中字符编码转换问题
    C# sealed关键词
  • 原文地址:https://www.cnblogs.com/Kingpenguin/p/10871446.html
Copyright © 2011-2022 走看看