AC自动机

作用

AC自动机全称Aho-Corasick自动机,这个算法是在KMP和trie的基础上得到的更优秀的字符串匹配算法(所以强烈建议先参考KMPTrie)。

实现

KMP是线性的匹配,AC自动机呢?不难想到在trie里匹配!
这里写图片描述
比如有she,her,hers,his,him这几个单词,我们需要在一个本文中查找这些单词。使用KMP需要每个单词先处理失配函数,然后还要遍历这个文本n次(n表示单词数量),能不能只遍历一次?这是有办法的:建立一棵trie,然后用trie来处理失配函数。

AC自动机查找和KMP类似,失配函数也非常类似,但是需要注意的是,失配函数的处理需要使用bfs拓展(也就是要一层一层来)。同时由于多个单词,在一个节点找到了单词,可能并不意味着没找到其他单词!所以我们还需要记录一个lst[i]表示离i节点最近的单词节点(但不能是i),这个在失配函数的构造中也可以处理出来。

用ΣL表示所有单词的总长度,n表示单词个数,len表示句子长度。那么n次KMP的复杂度就是O(ΣL+n*len),如果题目要求求出匹配总个数,那么复杂度可以被优化为O(ΣL+len)。

拓展

求出匹配总个数其实依靠的是lst的优化,即改变lst的定义,从“上一个位置”改为“前面的总个数”,如果题目要求输出所有方案,那么复杂度会退化为O(ΣL+匹配个数),在极端条件下复杂度和n次KMP的复杂度几乎一样。

模板

#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=10000,maxl=50,maxT=maxn*maxl,maxL=1000000,maxi=26;
//=======================================================================================
struct Node
{
    Node *son[maxi],*fa,*lst;
    int w;
};
typedef Node* P_node;
Node tem[maxT+5];
P_node null=tem,P_tem=null,que[maxT+5];
P_node newNode(P_node son)
{
    P_tem++;P_tem->fa=P_tem->lst=son;P_tem->w=0;
    for (int i=0;i<maxi;i++) P_tem->son[i]=son;
    return P_tem;
}
//=======================================================================================
int getid(char ch) {return ch-'a';}
struct AC //AC自动机结构体
{
    P_node ro;
    void clear() {ro=newNode(null);for (int i=0;i<maxi;i++) ro->son[i]=ro;ro->fa=ro->lst=ro;} //只要清空ro就可以了
    void Insert(char *s) //普通trie插入
    {
        P_node now=ro;
        for (int i=1;s[i];i++)
        {
            if (now->son[getid(s[i])]==ro) now->son[getid(s[i])]=newNode(ro);
            //直接指向ro可以理解为指向null,因为ro实际上不会作为儿子
            now=now->son[getid(s[i])];
        }
        now->w++;
    }
    void get_Fail()
    {
        int Head=0,Tail=0;
        for (int i=0;i<maxi;i++)
            if (ro->son[i]!=ro)
            {
                que[++Tail]=ro->son[i];
                ro->son[i]->fa=ro->son[i]->lst=ro;
            }
        while (Head!=Tail)
        {
            P_node x=que[++Head];
            for (int i=0;i<maxi;i++)
                if (x->son[i]!=ro)
                {
                    P_node now=x->son[i];now->fa=x->fa->son[i];
                    if (now->fa->w) now->lst=now->fa; else now->lst=now->fa->lst;
                    que[++Tail]=now;
                } else x->son[i]=x->fa->son[i]; //这里有个细节:如果和KMP一样写while do推fa会很繁琐,所以就直接改成while do的终点
                //2018.7.19UPD:其实这是记忆化,如果没有这句,处理一些操作的时候复杂度会出锅
        }
    }
    int get_num(P_node now)
    {
        int num=0;
        for (;now!=ro;now=now->lst) num+=now->w;
        return num;
    }
    int Find(char *s)
    {
        P_node j=ro;int num=0;
        for (int i=1;s[i];i++) j=j->son[getid(s[i])],num+=get_num(j);
        return num;
    }
};
//=======================================================================================
AC tr;
int te,n;
char now[maxL+5];
//=======================================================================================
bool Eoln(char ch) {return ch==10||ch==13||ch==EOF;}
int reads(char* s)
{
    int len=0;char ch=getchar();if (ch==EOF) return EOF;
    s[++len]=ch;while (!Eoln(s[len])) s[++len]=getchar();s[len--]=0;
    return 0;
}
void readln() {scanf("%*[^\n]");getchar();}
int main()
{
    freopen("AC.in","r",stdin);
    freopen("AC.out","w",stdout);
    scanf("%d",&te);
    while (te--)
    {
        scanf("%d",&n);readln();P_tem=null;tr.clear();
        for (int i=1;i<=n;i++) reads(now),tr.Insert(now);tr.get_Fail();
        reads(now);printf("%d\n",tr.Find(now));
    }
    return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Python AC自动机是一个用于字符串匹配的算法,它可以高效地在一段文本中查找多个预定义的模式。它的实现可以使用多种库,其中包括ac自动机python和ahocorasick-python。 ac自动机python是一个对标准的ac自动机算法进行了完善和优化的实现,适用于主流的Python发行版,包括Python2和Python3。它提供了更准确的结果,并且可以通过pip进行安装,具体的安装方法可以参考官方文档或者使用pip install命令进行安装。 ahocorasick-python是另一个实现AC自动机的库,它也可以用于Python2和Python3。你可以通过官方网站或者GitHub源码获取更多关于该库的信息和安装指南。 对于AC自动机的使用,一个常见的例子是在一段包含m个字符的文章中查找n个单词出现的次数。要了解AC自动机,需要有关于模式树(字典树)Trie和KMP模式匹配算法的基础知识。AC自动机的算法包括三个步骤:构造一棵Trie树,构造失败指针和模式匹配过程。在构造好AC自动机后,可以使用它来快速地在文本中查找预定义的模式,并统计它们的出现次数。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [ahocorasick-python:AC自动机python的实现,并进行了优化。 主要修复了 查询不准确的问题](https://download.csdn.net/download/weixin_42122986/18825869)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [Python实现多模匹配——AC自动机](https://blog.csdn.net/zichen_ziqi/article/details/104246446)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值