AC自动机——看似KMP在跑,其实fail在跳

先存代码

AC自动机(简单版)

  

#include<bits/stdc++.h>
#define maxn 1000007
using namespace std;
int n,ans;
int tr[maxn][28],val[maxn],cnt,fail[maxn];
char mod[maxn],tx[maxn];
queue<int >q;

void build(char *a){
    int now=0;
    for(int i=0;a[i];i++){
        if(!tr[now][a[i]-‘a‘]) tr[now][a[i]-‘a‘]=++cnt;
        now=tr[now][a[i]-‘a‘];
    }
    val[now]++;
}//建树 

void AC(){
    for(int i=0;i<26;i++) if(tr[0][i]) q.push(tr[0][i]);//26个字母跑
    while(!q.empty()){
        int u=q.front();q.pop();
        for(int i=0;i<26;i++){
            if(tr[u][i]) fail[tr[u][i]]=tr[fail[u]][i],q.push(tr[u][i]);//调取指针
            else tr[u][i]=tr[fail[u]][i];//建立“虚边”——指向失配指针的i边
            //这里已经改变了trie图
        }
    }
}

int query(char *t){
    int ol=0,u=0;
    for(int i=0;t[i];i++){
        u=tr[u][t[i]-‘a‘];
        for(int j=u;j&&val[j]!=-1;j=fail[j])
            ol+=val[j],val[j]=-1;//fail跳(这样其实很慢)
    }
    return ol;
}

int main(){
//    freopen("cin.in","r",stdin);
//    freopen("co.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%s",mod),build(mod);
    AC();
    scanf("%s",tx);ans=query(tx);
    printf("%d\n",ans);
}

AC自动机(加强版)

#include<bits/stdc++.h>
#define maxn 1000007
using namespace std;
int T,n,ans;
char mod[maxn][100],tx[maxn];

namespace AC{
    int tr[maxn][27],fail[maxn],tot;
    int cnt,val[maxn],num[maxn];
    void Init(){
        memset(tr,0,sizeof(tr));
        memset(num,0,sizeof(num));
        memset(fail,0,sizeof fail);
        memset(val,0,sizeof val);
        cnt=ans=0;
    }
    void insert(char *s,int id){
        int now=0;
        for(int i=0;s[i];i++){
            if(!tr[now][s[i]-‘a‘]) tr[now][s[i]-‘a‘]=++cnt;
            now=tr[now][s[i]-‘a‘];
        }
        val[now]=id;//记录id,这个不怕覆盖
    }
    queue<int >q;
    void build(){
        for(int i=0;i<26;i++) if(tr[0][i]) q.push(tr[0][i]);
        while(!q.empty()){
            int u=q.front();q.pop();
            for(int i=0;i<26;i++)
                if(tr[u][i]) fail[tr[u][i]]=tr[fail[u]][i],q.push(tr[u][i]),cerr<<fail[tr[u][i]]<<endl;
                else tr[u][i]=tr[fail[u]][i];
        }
    }
    void query(char *t){
        int u=0;
        for(int i=0;t[i];i++){
            u=tr[u][t[i]-‘a‘];
            for(int j=u;j;j=fail[j])
                if(val[j]) num[val[j]]++,ans=max(ans,num[val[j]]);
        }//还是跳,不过记录的不一样而已
        printf("%d\n",ans);
        for(int i=1;i<=n;i++) if(ans==num[i]) printf("%s\n",mod[i]);
    }
}

int main(){
    scanf("%d",&n);
    while(n){
        AC::Init();
        for(int i=1;i<=n;i++) scanf("%s",mod[i]),AC::insert(mod[i],i);
        AC::build();
        scanf("%s",tx);
        AC::query(tx);
        scanf("%d",&n);
    }
}

AC自动机(二次加强版)

#include<bits/stdc++.h>
#define maxn 2000007
using namespace std;
int n;
char mod[maxn],tx[maxn];
int fail[maxn],tr[maxn][27],val[maxn],num[maxn];
int id[maxn],cnt,in[maxn],to[maxn];

void insert(char *a,int idx){
    int now=0;
    for(int i=0;a[i];i++){
        if(!tr[now][a[i]-‘a‘]) tr[now][a[i]-‘a‘]=++cnt;
        now=tr[now][a[i]-‘a‘];
    }
    val[now]++;id[idx]=now;//记录
}

queue<int >q;

void build(){
    for(int i=0;i<26;i++) if(tr[0][i]) q.push(tr[0][i]);
    while(!q.empty()){
        int u=q.front();q.pop();
        for(int i=0;i<26;i++){
            if(tr[u][i]) fail[tr[u][i]]=tr[fail[u]][i],q.push(tr[u][i]),in[fail[tr[u][i]]]++;
            else tr[u][i]=tr[fail[u]][i];
        }
    }
}

void query(char *t){
    int u=0;
    for(int i=0;t[i];i++)
        u=tr[u][t[i]-‘a‘],num[u]++;
}

void topu(){
    for(int i=1;i<=cnt;i++) if(!in[i]) q.push(i);
    while(!q.empty()){
        int u=q.front(),v=fail[u];q.pop();
        num[v]+=num[u];--in[v];
        if(!(in[v])) q.push(v);
    }
}//这里是跟题解学的topu,效率也挺高 

int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%s",mod),insert(mod,i);
    build();
    scanf("%s",tx);query(tx);
    topu();
    for(int i=1;i<=n;i++) printf("%d\n",num[id[i]]);
}

单词

#include<bits/stdc++.h>
#define maxn 2000007
using namespace std;
int n;
char mod[maxn],tx[maxn],c[maxn];
int fail[maxn],tr[maxn][28],val[maxn],num[maxn];
int id[maxn],cnt,in[maxn],tot;

void insert(char *a,int idx){
    int now=0;
    for(int i=0;a[i];i++){
        if(!tr[now][a[i]-‘a‘]) tr[now][a[i]-‘a‘]=++cnt;
        now=tr[now][a[i]-‘a‘];
    }
    val[now]++;id[idx]=now;
}

queue<int >q;

void build(){
    for(int i=0;i<27;i++) if(tr[0][i]) q.push(tr[0][i]);
    while(!q.empty()){
        int u=q.front();q.pop();
        for(int i=0;i<27;i++){
            if(tr[u][i]) fail[tr[u][i]]=tr[fail[u]][i],q.push(tr[u][i]),in[fail[tr[u][i]]]++;
            else tr[u][i]=tr[fail[u]][i];
        }
    }
}

void query(char *t){
    int u=0;
    for(int i=0;t[i];i++)
        u=tr[u][t[i]-‘a‘],num[u]++;
}

void topu(){
    for(int i=1;i<=cnt;i++) if(!in[i]) q.push(i);
    while(!q.empty()){
        int u=q.front(),v=fail[u];q.pop();
        num[v]+=num[u];--in[v];
        if(!(in[v])) q.push(v);
    }
}

void work(char *a,char *b){
    int len1=strlen(a),len2=strlen(b);
    for(int i=len1;i<len1+len2;i++)
        a[i]=b[i-len1];
}

int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%s",c),work(mod,c),
        tot=strlen(mod),insert(c,i),mod[tot++]=‘{‘;
    build();
    query(mod);topu();
    for(int i=1;i<=n;i++) printf("%d\n",num[id[i]]);
}

原文地址:https://www.cnblogs.com/waterflower/p/11291421.html

时间: 2024-10-17 11:08:36

AC自动机——看似KMP在跑,其实fail在跳的相关文章

2017多校第8场 HDU 6138 Fleet of the Eternal Throne AC自动机或者KMP

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6138 题意:给n个串,每次询问x号串和y号串的最长公共子串的长度,这个子串必须是n个串中某个串的前缀 解法1:AC自动机.做法是把n个串建成AC自动机,前缀树中每个节点都当做结尾节点,val赋为trie树深度,然后把x串丢进自动机里,把匹配到的前缀节点染个色,再把y串丢进去,遇到同样颜色的前缀节点就更新一下答案. #include <bits/stdc++.h> using namespace s

AC自动机及KMP练习

好久都没敲过KMP和AC自动机了.以前只会敲个kuangbin牌板子套题.现在重新写了自己的板子加深了印象.并且刷了一些题来增加自己的理解. KMP网上教程很多,但我的建议还是先看AC自动机(Trie图)的构造后再去理解.板子的话大家大同小异. 而AC自动机的构造则是推荐王贇的<Trie图的构建.活用与改进>. 前面的备用知识则是字典树.推荐董华星的<浅析字母树在信息学竞赛中的应用>.董聚聚不仅仅是介绍了字典树,包括一些常见的应用也有论述,介绍的挺详细的. 接下来就是刷题的部分了.

BZOJ 题目3172: [Tjoi2013]单词(AC自动机||AC自动机+fail树||后缀数组暴力||后缀数组+RMQ+二分等五种姿势水过)

3172: [Tjoi2013]单词 Time Limit: 10 Sec  Memory Limit: 512 MB Submit: 1890  Solved: 877 [Submit][Status][Discuss] Description 某人读论文,一篇论文是由许多单词组成.但他发现一个单词会在论文中出现很多次,现在想知道每个单词分别在论文中出现多少次. Input 第一个一个整数N,表示有多少个单词,接下来N行每行一个单词.每个单词由小写字母组成,N<=200,单词长度不超过10^6

AC自动机初步

概述 应用场景:多模字符串匹配问题. KMP解决的问题是两个字符串之间的互相匹配,而如果有多个字符串要和一个字符串进行匹配呢?如果还用KMP的话,复杂度依然上天,所以,一个正常的想法是在KMP的基础上堆数据结构. 所以AC自动机=在Trie树上跑KMP,它其中也存在失配数组,与KMP类似. 初见 AC自动机的基础是Trie树.和Trie树不同的是,树中的每个结点除了有指向孩子的指针(或者说引用),还有一个fail指针,它表示输入的字符与当前结点的**所有孩子结点都不匹配时**(注意,不是和该结点

AC自动机详解

首先,看清楚了,这是AC自动机不是自动AC机 引用AC自动机模板题上的一句话: ovo 在学习AC自动机之前,应该先掌握一下两个前置技能: Trie KMP AC自动机,通俗地讲,就是在Trie上跑KMP.AC自动机利用Trie的性质和KMP的思想,可以实现字符串的多模匹配.KMP是单模匹配,而它与AC自动机最大的区别就在fail指针的求法,其余思想基本相同. 所谓多模匹配,即给出若干个模式串和一个文本串,需要查找这些模式串在文本串中出现的情况. AC自动机的操作分为三步: 建树 求fail指针

AC自动机--summer-work之我连模板题都做不出

这章对现在的我来说有点难,要是不写点东西,三天后怕是就一无所有了. 但写这个没有营养的blog的目的真的不是做题或提升,只是学习学习代码和理解一些概念. 现在对AC自动机的理解还十分浅薄,这里先贴上目前我看过的文章: 深入理解Aho-Corasick自动机算法 AC 自动机学习笔记 AC自动机相比Trie多了失配边,结点到结点间的状态转移,结点到根的状态转移. 这里fail的定义是:使当前字符失配时跳转到另一段从root开始每一个字符都与当前已匹配字符段某一个后缀完全相同且长度最大的位置继续匹配

[JSOI2007]文本生成器(AC自动机+DP)

题意 给你n个串.问有多少长度为m的串使得这n个串至少在其中出现过一次.输出答案膜10007意义下的结果. (n<=100,每个串的长度<=100) 题解 在AC自动机上跑DP. 用到一个容斥的思想,求至少出现过一次的次数就是,全部可能-一次都没出现的次数. 所以考虑dp,对于一个长度为i的串从i-1转移,i位置上的数必须不是n个串中某个串的结尾. 所以建立AC自动机,在上面跑.一个儿子从父亲转移时必须保证,儿子节点和儿子节点通过fail指针间接到达的点,都不是终止节点. 具体看代码. 1 #

强势图解AC自动机

本篇文章主要详细介绍$AC$自动机的$fail$指针: 如果有什么不完善的地方,请联系我$qwq$ 前置知识: 1.建议学一下$kmp$算法 2.$Trie$ 导入: AC自动机是用来解决多模板匹配问题的,但是,如果就单纯的把每个模板串拼接在一起,或者单个单个匹配的话,肯定是会超时的,而它的思想是把所有的模式串建立一颗$Trie$,然后用文本串来匹配,那么我们就必须在这颗$Trie$树上进行快速跳转来优化,于是,AC自动机就诞生了 重点:fail指针到底是什么? 我们先来思考一个问题,加入我们按

图解AC自动机

图解AC自动机 前言: 我们引出这样一个问题: 我想知道字符串\(t\)在字符串中\(s\)出现多少次/有没有出现? 那我们可以使用kmp算法求出\(t\)的next数组,之后\(O(n)\)匹配求解即可. 那如果把问题升级一下呢? 想知道字符串\(t_1,t_2,...,t_n\)在字符串\(s\)中出现了多少次/有没有出现? 这时候再用\(kmp\)算法,复杂度将达到\(O(n^2)\),非常慢了. 这时候我们需要AC自动机. AC自动机用于求解多个模式串与一个文本串的匹配问题. AC自动机