AC自动机

推荐blog

 认为上面这篇blog已经讲述的十分清楚(一定要画图多手动模拟,,才能感受AC自动机的奥妙),所以解释两个我想了很久也没想明白的问题 (是基于代码的)

1.为什么要用BFS太蠢了QWQ

2.连接fail时的一些特殊情况到底是怎么处理的

例如当前有一颗这样的树:

我们已经按照操作,搜索到了标红节点,按照AC自动机的思想连fail边,因为我们已经处理过它的父亲节点c节点的fail值,所以d节点的fail值应该等于。。讲不清了大概是这样连边:

然而虚拟d点的编号为0,那这样红色d点岂不是相当于连到了顶点,但其实不是,我们也可以看出来d点应该连在第3列的d点上

但这是怎么处理的呢?因为是BFS,是按照深度由浅到深进行处理的,所以虚拟点其实已经处理过且值已变成另一个d点了

就像是这样:

推荐题目:洛谷P3808

代码如下:

#include<bits/stdc++.h>
using namespace std;
int num,ans;
struct{
    int vis[28];
    int fail;
    int end;
}a[1000006];
void build(string s){
    int i,l,now=0;
    l=s.length();
    for(i=0;i<l;i++){
        if(a[now].vis[s[i]-'a']==0){
            num++;
            a[now].vis[s[i]-'a']=num;
        }
        now=a[now].vis[s[i]-'a'];
    }
    a[now].end++;
}
void fail(){
    queue<int>q;
    int now,i;
    for(int i=0;i<26;++i){
          if(a[0].vis[i]!=0)
              q.push(a[0].vis[i]);
    }//一定要有,若只是将0放入队列中,那第一层字母将会指向它自己,然而它应该指向0点
    while(!q.empty()){
        now=q.front();q.pop();
        for(i=0;i<26;i++){
            if(a[now].vis[i]){
                a[a[now].vis[i]].fail=a[a[now].fail].vis[i];
                q.push(a[now].vis[i]);
            }
            else a[now].vis[i]=a[a[now].fail].vis[i];
        }
    }
}
void find(string s){
    int i,l,now,j;
    l=s.length();
    now=0;
    for(i=0;i<l;i++){
            now=a[now].vis[s[i]-'a'];
            for(j=now;j&&a[j].end!=-1;j=a[j].fail){
                ans+=a[j].end;
                a[j].end=-1;
            }
    }
}
int main(){
    int i,j,l,n;
    string s;
    cin>>n;
    for(i=1;i<=n;i++){
        cin>>s;
        build(s);
    }
    fail();
    //for(i=1;i<=num;i++)printf("%d %d ",a[i].fail,a[i].end);
    cin>>s;
    find(s);
    printf("%d",ans);
}
原文地址:https://www.cnblogs.com/Jessica-Cao/p/13551841.html