您的位置:首页 > 其它

AC 自动机(模板)

2017-04-16 11:24 302 查看
AC自动机 就是一个多子串匹配的东西,哪有那么厉害,和kmp一样有一个fail指针(下面代码打错了。。,不改了),匹配失败了就跳到file指针,因为fail指针都是从父节点的fail指针推过来的,这样就可以保证前面是一样的了。

AC自动机分三步,第一步是建trie树,第二步是建fail指针(最关键的一步,否则就和字典树一样了),第三步是匹配。

1.建trie图

代码在这



很容易就建完了。

2.建fail指针



相对较难理解的

有三个变量(都是编号,不是字符)

1 父节点(就是出队的)

2 子节点(就是父结点的子节点)

3 file指向的节点(就是父节点的fail指针,下面简称1,2,3)

其实就是用队列实现的,先把根节点的子节点入队,在一个个出队,如果3.next[2]有值,出队时将2的fail指针连向3的next[2],没有就把继续找3的fail,直到fail.next[2]有值,或到了根节点。

3.匹配

有一个变量表示当前字符

遵循两种情况
1.原串字符和当前指针匹配,就继续向下去匹配吧
2.不匹配,就把当前指针移到当前指针的fail指针上,再继续匹配,直到根节点。


这样AC自动机就完成了

丑陋的代码在这

#include<cstdio>
#include <iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define id(x) x-'a';
using namespace std;
struct st{
int nex[26];
int file;
int count;
int innn(){
memset(nex, -1, sizeof(nex));
count=0;
file=0;
}
}s[9999];
int cnt;
int ins(char ss[]){
int p=0;
for(int i=0;i<strlen(ss);i++){
int x=id(ss[i]);
<
4000
span class="hljs-keyword">if(s[p].nex[x]==-1)
{
s[cnt].innn();
s[p].nex[x]=cnt++;
}
p=s[p].nex[x];
}
s[p].count++;
}
int d[999999];
int make_file(){
int tail=0,head=0;
for(int i=0;i<=25;i++)
{
if(s[0].nex[i]!=-1){
d[tail++]=s[0].nex[i];
}
}
while(head!=tail){

int x=d[head];head++;
for(int i=0;i<=25;i++){
if(s[x].nex[i]!=-1){
d[tail++]=s[x].nex[i];
int tmp=s[x].file;
while(s[tmp].nex[i]==-1&&tmp>0){
tmp=s[tmp].file;
}
if(s[tmp].nex[i]!=-1){
tmp=s[tmp].nex[i];
}
s[s[x].nex[i]].file=tmp;
}
}
}
}
int tot;char sr[9999];
char sh[999];
int n;
int pipei(){
int p=0;
for(int i=0;i<strlen(sr);i++)
{
int x=id(sr[i]);
while(p>0&&s[p].nex[x]==-1){
p=s[p].file;
}
if(s[p].nex[x]!=-1){
p=s[p].nex[x];
//  tot+=s[p].count;
//  s[p].count=0;
int ind=p;
while(ind > 0 && s[ind].count != -1)
{
tot += s[ind].count;
s[ind].count = -1;
ind = s[ind].file;
}
}
}
printf("%d",tot);
return 0;
}

int main(){
gets(sr);
s[0].innn();cnt=1;
scanf("%d\n",&n);
while(n--){
scanf("%s",sh);
//printf("%s\n",sh);
ins(sh);
}
make_file();
pipei();
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: