• AC自动机


    AC自动机就是用来处理在一个字符串中找多个模式串的问题。

    假设有N个模式串,平均长度为L;文章长度为M。 建立Trie树:O(N*L) 建立fail指针:O(N*L) 模式匹配:O(M*L) 所以,总时间复杂度为:O( (N+M)*L )。

    典型例题见洛谷上这道模板题 https://www.luogu.org/problemnew/show/P3808

    AC自动机相当于在Trie树上进行KMP,因为KMP每次只能对一个模式串进行匹配,而使用Trie树可以把多个模式串存入树中。

    其中一大关键就是fail指针,相当于KMP中的next数组。他表示的就是当文本串和模式串失配的时候要跳转的节点。

    以这张图为例,文本串是ushers,模式串是he, she, his, hers

    当一个节点失配时,他的fail指针应指向他的父节点的fail指针指向节点的对应的子节点。

    当一个节点没有对应的子节点时,应该让他的子节点指向他的fail指针指向的节点的对应子节点。

    用BFS求出整棵树的fail指针。

    query时就是不断沿着fail指针进行匹配。【类比KMP】

    附上洛谷P3808AC代码

    https://www.luogu.org/problemnew/show/P3808

     1 #include <iostream>
     2 #include <set>
     3 #include <cmath>
     4 #include <stdio.h>
     5 #include <cstring>
     6 #include <algorithm>
     7 #include <vector>
     8 #include <queue>
     9 #include <map>
    10 using namespace std;
    11 typedef long long LL;
    12 #define inf 0x7f7f7f7f
    13 
    14 int n;
    15 const int maxn = 1e6 + 5;
    16 
    17 struct Tree{
    18     int fail;//失配指针
    19     int vis[26];//子节点位置
    20     int ed;//标记有几个单词以这个节点结尾
    21 }AC[maxn];
    22 int cnt = 0;
    23 
    24 void build(string s)
    25 {
    26     int len = s.length();
    27     int now = 0;//字典树当前指针
    28     for(int i = 0; i < len; i++){
    29         if(AC[now].vis[s[i] - 'a'] == 0){
    30             AC[now].vis[s[i] - 'a'] = ++cnt;
    31         }
    32         now = AC[now].vis[s[i] - 'a'];
    33     }
    34     AC[now].ed += 1;
    35 }
    36 
    37 void get_fail()
    38 {
    39     queue<int> que;
    40     for(int i = 0; i < 26; i++){
    41         if(AC[0].vis[i] != 0){
    42             AC[AC[0].vis[i]].fail = 0;
    43             que.push(AC[0].vis[i]);
    44         }
    45     }
    46     while(!que.empty()){
    47         int u = que.front();
    48         que.pop();
    49         for(int i = 0; i < 26; i++){
    50             if(AC[u].vis[i] != 0){
    51                 AC[AC[u].vis[i]].fail = AC[AC[u].fail].vis[i];
    52                 que.push(AC[u].vis[i]);
    53             }
    54             else{
    55                 AC[u].vis[i] = AC[AC[u].fail].vis[i];
    56             }
    57         }
    58     }
    59 }
    60 
    61 int AC_query(string s)
    62 {
    63     int len = s.length();
    64     int now = 0, ans = 0;
    65     for(int i = 0; i < len; i++){
    66         now = AC[now].vis[s[i] - 'a'];
    67         for(int t = now; t && AC[t].ed != -1; t = AC[t].fail){
    68             ans += AC[t].ed;
    69             AC[t].ed = -1;
    70         }
    71     }
    72     return ans;
    73 }
    74 
    75 int main()
    76 {
    77     scanf("%d", &n);
    78     string s;
    79     for(int i = 0; i < n; i++){
    80         cin>>s;
    81         build(s);
    82     }
    83     AC[0].fail = 0;
    84     get_fail();
    85     cin>>s;
    86     cout<<AC_query(s)<<endl;
    87     return 0;
    88 }

    在附上洛谷P3796的模板题代码,这题统计多个模式串的出现次数。只需要把end改一下就好了。

    https://www.luogu.org/problemnew/show/P3796

      1 #include <iostream>
      2 #include <set>
      3 #include <cmath>
      4 #include <stdio.h>
      5 #include <cstring>
      6 #include <algorithm>
      7 #include <vector>
      8 #include <queue>
      9 #include <map>
     10 using namespace std;
     11 typedef long long LL;
     12 #define inf 0x7f7f7f7f
     13 
     14 int n;
     15 const int maxn = 155 * 80;
     16 
     17 struct Tree{
     18     int fail;//失配指针
     19     int vis[26];//子节点位置
     20     int ed = -1;//标记有几个单词以这个节点结尾
     21 }AC[maxn];
     22 string s[155];
     23 int cnt[155];
     24 int tot = 0;
     25 
     26 void build(string s, int id)
     27 {
     28     int len = s.length();
     29     int now = 0;//字典树当前指针
     30     for(int i = 0; i < len; i++){
     31         if(AC[now].vis[s[i] - 'a'] == 0){
     32             AC[now].vis[s[i] - 'a'] = ++tot;
     33         }
     34         now = AC[now].vis[s[i] - 'a'];
     35     }
     36     AC[now].ed = id;
     37 }
     38 
     39 void get_fail()
     40 {
     41     queue<int> que;
     42     for(int i = 0; i < 26; i++){
     43         if(AC[0].vis[i] != 0){
     44             AC[AC[0].vis[i]].fail = 0;
     45             que.push(AC[0].vis[i]);
     46         }
     47     }
     48     while(!que.empty()){
     49         int u = que.front();
     50         que.pop();
     51         for(int i = 0; i < 26; i++){
     52             if(AC[u].vis[i] != 0){
     53                 AC[AC[u].vis[i]].fail = AC[AC[u].fail].vis[i];
     54                 que.push(AC[u].vis[i]);
     55             }
     56             else{
     57                 AC[u].vis[i] = AC[AC[u].fail].vis[i];
     58             }
     59         }
     60     }
     61 }
     62 
     63 void AC_query(string s)
     64 {
     65     int len = s.length();
     66     int now = 0, ans = 0;
     67     for(int i = 0; i < len; i++){
     68         now = AC[now].vis[s[i] - 'a'];
     69         for(int t = now; t; t = AC[t].fail){
     70             if(AC[t].ed != -1){
     71                 cnt[AC[t].ed]++;
     72             }
     73 
     74             //ans += AC[t].ed;
     75             //AC[t].ed = -1;
     76         }
     77     }
     78     //return ans;
     79 }
     80 
     81 int main()
     82 {
     83     while(scanf("%d", &n) != EOF && n){
     84         memset(cnt, 0, sizeof(cnt));
     85         tot = 0;
     86         for(int i = 0; i < maxn; i++){
     87             AC[i].fail = 0;
     88             AC[i].ed = -1;
     89             for(int j = 0; j < 26; j++){
     90                 AC[i].vis[j] = 0;
     91             }
     92         }
     93         for(int i = 0; i < n; i++){
     94             cin>>s[i];
     95             build(s[i], i);
     96         }
     97         AC[0].fail = 0;
     98         get_fail();
     99         string str;
    100         cin>>str;
    101         AC_query(str);
    102         int maxs = -1, maxid = 0;
    103         for(int i = 0; i < n; i++){
    104             if(maxs < cnt[i]){
    105                 maxs = cnt[i];
    106             }
    107         }
    108         cout<<maxs<<endl;
    109         for(int i = 0; i < n; i++){
    110             if(cnt[i] == maxs){
    111                 cout<<s[i]<<endl;
    112             }
    113         }
    114     }
    115 
    116     //cout<<s[maxid]<<endl;
    117     //cout<<AC_query(s)<<endl;
    118     return 0;
    119 }
  • 相关阅读:
    【解题报告】NOIP2018
    【解题报告】NOIP2013
    【解题报告】NOIP2014
    【解题报告】NOIP2015
    【解题报告】NOIP2016
    【解题报告】CSPS2020
    【全程NOIP计划】初级数据结构1
    如何通过一个结构体成员变量的地址找到该结构体的首地址?[备忘]
    IGT笔试题,正整数N等于M个不同的正整数之和的问题
    Mac下Perl脚本如何运行
  • 原文地址:https://www.cnblogs.com/wyboooo/p/9851983.html
Copyright © 2020-2023  润新知