【算法与数据结构】——AC自动机

参考文章AC自动机
感觉这篇文章讲的十分简洁易懂,但是好像有点地方的表述有点错误。我决定根据自己的理解再复述一次整个ac自动机的思路

AC自动机

ac自动机是在字典树和KMP算法的基础上进行拓展的,KMP算法属于是单模匹配算法,AC自动机属于是多模匹配算法。

原理

首先给定模式串"ash",“shex”,“bcd”,“sha”
在这里插入图片描述
AC自动机就是在trie树的基础上,增加一个fail指针(相当于kmp中的next数组),如果当前点匹配失败,则将指针转移到fail指针指向的地方,这样就不用回溯,而可以路匹配下去了。

一般fail指针的构建是用bfs实现的,首先每个模式串的首字母肯定是指向根节点的。
在这里插入图片描述
现在第一层bfs遍历完了,开始第二层
(根节点为第0层)第二层a的子节点为s,但是我们还是要从a-z遍历,如果不存在这个子节点我们就让这个子节点指向当前节点fail指针的这个子节点)
请添加图片描述
当我们遍历当前节点子节点遍历到s的时候,由于存在s这个节点,我们就让s这个子节点的fail指针指向当前节点(a)的fail指针指向的那个节点的具有相同字母的子节点(第一层的s),也就是这样
请添加图片描述
后面的层规则与之前是相同的,以此类推即可。
完全构造好的树是这样的。
请添加图片描述
关于字符串匹配的过程就是跟kmp算法是一样的了,遇到适配字符就跳转到fail指针指向的位置即可。

模板

#include <bits/stdc++.h>
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
const int N=2e6+6;
int trie[N][26];
int tot;
int cntword[N];
int fail[N];
void insertWords(string str)
{
    int p=0;
    for(int i = 0;i < str.size();i++)
    {
        int next = str[i]-'a';
        if(trie[p][next]==0)
        {
            trie[p][next]=++tot;
        }
        p=trie[p][next];
    }
    cntword[tot]++;
}
void getFail()
{
    queue<int> q;
    for(int i=0;i < 26;i++)
    {
        if(trie[0][i])
        {
            fail[trie[0][i]]=0;
            q.push(trie[0][i]);
        }
    }

    while(!q.empty())
    {
        int now= q.front();
        q.pop();

        for(int i = 0;i < 26;i++)
        {
            if(trie[now][i]){//子节点存在
                fail[trie[now][i]]=trie[fail[now]][i];
                q.push(trie[now][i]);
            }
            else//子节点不存在
            {
                trie[now][i]=trie[fail[now]][i];
            }
        }
    }
}
int query(string s)//查询字符串s中有几个单词
{
    int now = 0,ans = 0;
    for(int i=0;i<s.size();i++){
        now = trie[now][s[i]-'a'];
        for(int j=now;j && cntword[j]!=-1;j=fail[j]){
            ans += cntword[j];
            cntword[j] = -1;
        }
    }
    return ans;
}

int main()
{
    insertWords("in");
    insertWords("name");
    insertWords("na");
    insertWords("string");
    insertWords("int");
    fail[0] = 0;
    getFail();
    cout << query("nameisinstring") << endl;//4
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值