ac自动机初学笔记

最近在做题的过程中遇到一道题,它要求找出一个字符串中出现了多少中之前给的模式字符串,刚刚看到这个题的时候,
第一反应就是暴力,但是仔细想想,怎么可能是暴力呢,后来就去看了博客,才知道这是ac自动机

1.什么是ac自动机

ac自动机是一种用于多模式匹配的数据结构

2.为什么要使用ac自动机?

因为使用ac自动机会更快的解决多模式匹配的问题

原理

用我自己的话来说,就是,在已经构建的模式串的字典树中进行主串匹配,如果在当前匹配过程中,当前字符可以
匹配,那么就继续沿着这条分支继续匹配,如果不匹配的话,那么就跳到之前我们预处理过的当失败的时候该跳的节点那里去
(他们都叫它失败指针),当然我们要为每一个到过的节点进行标记,如果当前的节点是一个模式串的结尾的话,那么就说明
这个模式串在主串中出现过
按照刚刚我说的,我们要做的工作有
1.构建模式串的字典树:

构建字典树的话比较简单,可以看看代码

2.构建失败指针

​ 为什么要构建失败指针呢?仔细想想,如果当前字符失配了,就说明当前分支的这个字符串就能匹配,就应该跳到下一个模式串,而对于下一个模式串的话,可以有两种情况,一是这个模式串的前缀与当前失配的字符串的后缀有相同的部分,二是没有相同的部分。如果有第一种情况,那么是最好的,我们就跳到那个模式串的公共部分的那个节点之后,这样我们又省去一些时间,而这里的那个节点就被我们称为失败指针。而失败指针是如何构造的呢?这里是采用bfs来构造的,简单的来说:

×如果是第二层的节点,他们的失败指针都是root

×对于接下来的每一层,每个节点都在父节点的失败指针里面找有没有与自己相等的节点,如果有,就指向它,如果没有,就指向root;

3.匹配过程

匹配过程就是如果,遇到不匹配的就跳失败指针

匹配的就继续沿着分支匹配,如果遇到结尾就记录

— 说的有点简单,主要是理解的还不是很升入,后面多加练习了后再来完善

模板:洛古p3830

#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
const int maxn=2e6+5;
int node[maxn],vis[maxn],n,ans;
int trie[maxn][26],fail[maxn],cnt=1;
int insert(string a)
{
     int len=a.size();
     int id,now=1,i;
     for(i=0;i<len;i++)
        {
            id=a[i]-'a';
            if(trie[now][id])now=trie[now][id];
            else trie[now][id]=++cnt,now=cnt;
        }
     return cnt;
}
void create()
{
     queue<int>q;
     q.push(1);
     int now,p,v,i;
     while(!q.empty())
        {
            now=q.front();q.pop();
            for(i=0;i<26;i++)
            {
                if(trie[now][i]==0)continue;
                v=trie[now][i],p=fail[now];
                while(p&&!trie[p][i])p=fail[p];//为什么要这样写?可以自己思考一下
                if(!p)fail[v]=1;
                else fail[v]=trie[p][i];
                q.push(v);
            }
        }
}

void work(string a)
{
    int len=a.size(),now=1,id,p,i;
    for(i=0;i<len;i++)
        {
            id=a[i]-'a',vis[now]=1;
            while(now&&!trie[now][id])now=fail[now];
            if(now)now=trie[now][id];else now=1;
            p=fail[now];
            while(p&&!vis[p])vis[p]=1,p=fail[p];

        }
     vis[now]=1;
}


int main()
{
    string s;
    int i,j,k,ans=0;
    cin>>n;
    for(i=0;i<n;i++)cin>>s,node[i]=insert(s);
    create();cin>>s,work(s);
    for(i=0;i<n;i++)if(vis[node[i]])++ans;
    cout<<ans<<endl;
    return 0;

}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值