ac自动机,是用于计算多个字符串匹配的算法,我们知道,
K
M
P
KMP
KMP以一种及其巧妙的方式实现了以单模字符串的匹配(以单个字符串去询问其他字符串中是否出现该字符串),现在我们要实现多模的字符串匹配(即以多个字符串去询问其他字符串中是否出现了这些字符串),用
K
M
P
KMP
KMP显然是不足够的,那么就需要一种新的算法继承了
K
M
P
KMP
KMP的思想,还要建立在一个数据结构上,它便是
t
r
i
e
trie
trie字典树。
字典树是将字符串分解为单个字符并存在树上的一种数据结构,例如,我们有
{“ab”,“ac”,“ba”,“cba”}四个字符串,我们便可以构建如下的一棵树来存储这些字符串。
p
s
:
ps:
ps:由于是前置知识,在这里便不多赘述。
下面,我们该如何将这些字符串与另外的字符串如
c
b
b
a
cbba
cbba匹配呢?我们可以暴力去搜树,但这样会浪费巨量的时间和空间,于是我们引入了这样的一个概念失配指针,例如我们去查数时,cb首先在最右边的子树上查到了,但是a却和字符串中的b不相匹配,重新查又觉得很浪费,这时我们看到中间的子树有个b,我们便可以跳转到中间的子树继续向下查询,由此推广,我们每次看到附近自根伸出的子树与当前正在查询的子树构成字符串的最大后缀相等时,我们便可以通过失配指针跳转到后来的子树继续查询来最大限度的避免浪费,由于后面的失配指针肯定是需要前面的失配指针来得到的,所以我们考虑用队列来完成每个节点失配指针的获取,具体如图所示:
下面给出一道模板题和ac代码:
题目传送门:hdu2222 Keywords Search
a
c
ac
ac代码:
#include<bits/stdc++.h>
using namespace std;
#define MAX 1000009
#define TOTAL 500009
struct Aho{
struct state{
int next[26];
int fail,cnt;
}stateTable[TOTAL];
int size;
queue<int>q;
void init(){
for(int i=0;i<TOTAL;i++){
memset(stateTable[i].next,0,sizeof stateTable[i].next);
stateTable[i].fail=stateTable[i].cnt=0;
}
size = 1;
}
void insert(char *S){
int n = strlen(S);
int now = 0;
for(int i=0;i<n;++i){
char c = S[i];
if(!stateTable[now].next[c-'a'])
stateTable[now].next[c-'a'] = size++;
now = stateTable[now].next[c-'a'];
}
stateTable[now].cnt++;
}
void build(){
stateTable[0].fail = -1;
q.push(0);
while(q.size()){
int u = q.front();
q.pop();
for(int i=0;i<26;++i){
if(stateTable[u].next[i]){
if(u == 0) stateTable[stateTable[u].next[i]].fail = 0;
else{
int v = stateTable[u].fail;
while(v!=-1){
if(stateTable[v].next[i]){
stateTable[stateTable[u].next[i]].fail = stateTable[v].next[i];
break;
}
v = stateTable[v].fail;
}
if(v == -1) stateTable[stateTable[u].next[i]].fail = 0;
}
q.push(stateTable[u].next[i]);
}
}
}
}
int Get(int u){
int res = 0;
while(u){
res = res + stateTable[u].cnt;
stateTable[u].cnt = 0;
u = stateTable[u].fail;
}
return res;
}
int match(char *S){
int n = strlen(S);
int res = 0, now = 0;
for(int i=0;i<n;++i){
char c =S[i];
if(stateTable[now].next[c-'a']){
now = stateTable[now].next[c-'a'];
}
else{
int p = stateTable[now].fail;
while(p!=-1&&stateTable[p].next[c-'a']==0) p = stateTable[p].fail;
if(p==-1) now = 0;
else now = stateTable[p].next[c-'a'];
}
if(stateTable[now].cnt){
res += Get(now);
}
}
return res;
}
}aho;
char S[MAX];
int main(){
int T;
scanf("%d",&T);
while(T--){
aho.init();
int n;scanf("%d",&n);
for(int i=0;i<n;++i){
scanf("%s",S);
aho.insert(S);
}
aho.build();
scanf("%s",S);
printf("%d\n",aho.match(S));
}
return 0;
}
以上(什么嘛,还是很简单的嘛( ̄▽ ̄)/)