【暑假】[实用数据结构] AC自动机

Aho-Corasick自动机

 算法:

 <功能>

  AC自动机用于解决文本一个而模板有多个的问题。

  AC自动机可以成功将多模板匹配,匹配意味着算法可以找到每一个模板在文本中出现的位置。

 <解释>

  KMP中对模板构造失配边,多模板每条模板独立构造失配边太过麻烦。

  算法利用Trie+KMP中的失配边。insert(模板) 构造Trie+ getFail添加失配边->AC自动机的状态转移图。

  匹配文本串text时只需要调用find,find依次匹配text中的每一个字符失败则沿着失配边走,在匹配路径上如果遇到单词结点(val != 0 )即相当于匹配成功。

  但需要注意到:可能有作为当前后缀的单词已经成功匹配,所以需要加入后缀链接last[] 在每一个结点都要处理这种情况。last递推得到。

作者所给模板如下

 1 struct AhoCorasickAutomata {
 2   int ch[MAXNODE][SIGMA_SIZE];
 3   int f[MAXNODE];    // fail函数
 4   int val[MAXNODE];  // 每个字符串的结尾结点都有一个非0的val
 5   int last[MAXNODE]; // 输出链表的下一个结点
 6   int cnt[MAXS];
 7   int sz;
 8 
 9   void init() {
10     sz = 1;
11     memset(ch[0], 0, sizeof(ch[0]));
12     memset(cnt, 0, sizeof(cnt));
13     ms.clear();
14   }
15 
16   // 字符c的编号
17   int idx(char c) {
18     return c-'a';
19   }
20 
21   // 插入字符串 v必须非0
22   void insert(char *s, int v) {
23     int u = 0, n = strlen(s);
24     for(int i = 0; i < n; i++) {
25       int c = idx(s[i]);
26       if(!ch[u][c]) {
27         memset(ch[sz], 0, sizeof(ch[sz]));
28         val[sz] = 0;
29         ch[u][c] = sz++;
30       }
31       u = ch[u][c];
32     }
33     val[u] = v;
34     ms[string(s)] = v;
35   }
36 
37   // 递归打印以结点j结尾的所有字符串
38   void print(int j) {
39     if(j) {
40       cnt[val[j]]++;
41       print(last[j]);
42     }
43   }
44 
45   // 在T中找模板
46   int find(char* T) {
47     int n = strlen(T);
48     int j = 0; // 当前结点编号 初始为根结点
49     for(int i = 0; i < n; i++) { // 文本串当前指针
50       int c = idx(T[i]);
51       while(j && !ch[j][c]) j = f[j]; // 顺着细边走 直到可以匹配
52       j = ch[j][c];
53       if(val[j]) print(j);
54       else if(last[j]) print(last[j]); // 找到了 
55     }
56   }
57 
58   // 计算fail函数
59   void getFail() {
60     queue<int> q;
61     f[0] = 0;
62     // 初始化队列
63     for(int c = 0; c < SIGMA_SIZE; c++) {
64       int u = ch[0][c];
65       if(u) { f[u] = 0; q.push(u); last[u] = 0; }
66     }
67     // 按BFS顺序计算fail
68     while(!q.empty()) {
69       int r = q.front(); q.pop();
70       for(int c = 0; c < SIGMA_SIZE; c++) {
71         int u = ch[r][c];
72         if(!u) continue;
73         q.push(u);
74         int v = f[r];
75         while(v && !ch[v][c]) v = f[v];
76         f[u] = ch[v][c];
77         last[u] = val[f[u]] ? f[u] : last[f[u]];
78       }
79     }
80   }
81 
82 };
原文地址:https://www.cnblogs.com/lidaxin/p/4719186.html