ps:学习AC自动机的时候必须先点亮KMP和字典树的技能点。

步骤:

1.将所有模式串建立一个字典树。

2.对字典树建立失配边。

3.用目标串与字典树进行匹配。

写法:

现在通用的AC自动机写法有两类:1.数组。2.指针。(也有把这两种结合到一起的写法)

指针比较直观,但是做有的题时不好操作;数组相对抽象些,但是适用范围广。我用的是数组的写法,借鉴的是kuangbin大神的板子。

例子:

举个例子,有模式串:she  he  say  shr  her

目标串:yasherhs

现在我们要用这5个模式串建立字典树,并构建失配边。如下图:

代码:(对AC自动机的解释都在代码注释中)

#include <stdio.h> #include <string.h> #include <iostream> #include <algorithm> #include <queue> #include <set> #include <stdlib.h> using namespace std; const int maxn = 500010; struct Trie{ //next存放的是字典树,在上图中是实线,next[i][j]中i是父亲节点,j是孩子节点 //fail存放失配边的信息,在上图中是蓝线 //end标记的是每个模式串末尾节点,在上图中用红色表示 int next[maxn][26],fail[maxn],end[maxn]; int root,L;//root是根节点,L是节点编号 //newnode()创建一个新的节点,并将它的所有孩子节点初始化为-1,返回这个新建节点的编号 int newnode(){ for(int i = 0;i < 26;i++)
            next[L][i] = -1;
        end[L++] = 0; return L-1;
    } //初始化,给根节点初始化为0号节点 void init(){
        L = 0;
        root = newnode();
    } //建立字典树 void insert(char buf[]){ int len = strlen(buf); int now = root;//从根节点开始建立树 for(int i = 0;i <len;i++){ if(next[now][buf[i]-'a'] == -1)//如果现在这个节点下面没有这个节点,就建立新的节点 next[now][buf[i]-'a'] = newnode();
            now = next[now][buf[i]-'a'];//切换到下一个节点 }
        end[now]++;//标记末尾节点 } //建立失配边,用bfs的方式建立 void build(){ queue<int> Q;
         fail[root] = root; for(int i = 0;i < 26;i++){ if(next[root][i] == -1)
                next[root][i] = root; else{
                fail[next[root][i]] = root;//如果没有前缀,就指向root就行了 Q.push(next[root][i]);
            }
         } while(!Q.empty()){ int now = Q.front();
            Q.pop(); for(int i = 0;i < 26;i++){ if(next[now][i] == -1)
                    next[now][i] = next[fail[now]][i]; else{
                    fail[next[now][i]] = next[fail[now]][i];//失配边指向相同前缀 Q.push(next[now][i]);
                }
            }
         }
    } //目标串与字典树进行匹配 int query(char buf[]){ int len = strlen(buf); int now = root; int res = 0; for(int i = 0;i < len;i++){
            now = next[now][buf[i]-'a']; int temp = now; //找目标串中与模式串相同的串 while(temp != root){
                res += end[temp];
                end[temp] = 0;//这里置零是防止重复计数 temp = fail[temp];
            }
        } return res;//返回匹配成功的个数 } //debug函数,不做多解释了 void dubug(){
    }
}; char buf[1000010];
Trie ac; int main() { int t; int n; scanf("%d",&t); while(t--){ scanf("%d",&n);
        ac.init();//初始化 for(int i = 0;i < n;i++){ scanf("%s",buf);
            ac.insert(buf);
        }
        ac.build(); scanf("%s",buf); printf("%d\n",ac.query(buf));
    } return 0;
}