zoukankan      html  css  js  c++  java
  • 【模板】AC自动机(简单版)

    我:“woc。。。AC自动机?”

    我:“可以自动AC???”

    然鹅。。。

    大佬:“傻。。。”

    我:“(⊙_⊙)?”

    大佬:“缺。。。”

    我:“。。。。。。”

    (大佬。。。卒 | 逃。。。)

    emm。。。好吧。。。让我们来看看AC自动机是啥。。。

    写在前面:如果没有学过 KMP 和 Trie树,请先理解这两个算法。。。

    这个东西因为是一个歪果仁发明,然后两个单词的首字母为AC,所以就被叫做AC自动机了。。。

    这个东西简单来说 AC自动机 == Trie + KMP

    我们需要维护的东西也很简单,类似于Trie树上的KMP中的next数组。。。(作用类似,代表意义不同)

    在AC自动机中有一个 fail 叫做失配指针。。。就是用它来进行跳转进行匹配

    这样我们就可以发现。。。

    Trie树维护多个字符串的功能 + KMP判子串的功能 == AC自动机匹配多个字符串

    emm。。。讲解原理比较麻烦,直接看代码和注释比较好懂,当然建议根据大体的原理先瞎搞一波再看

    呆码:

    #include<iostream>
    #include<cstdio>
    #include<queue>
    #include<cstring>
    using namespace std;
    
    struct asd{
        int fail; // 失配指针
        int vis[26]; // 子节点的位置
        int end; // 标记有几个单词以这个节点结尾 
    } AC[1000010];
    int rt,cnt,l,n;
    char ch[1000010];
    
    inline void build(char *ch)
    {
        int l=strlen(ch+1);
        int now=rt,v;
        for(int i=1;i<=l;i++)
        {
            v=ch[i]-'a';
            if(!AC[now].vis[v]) AC[now].vis[v]=++cnt;
            now=AC[now].vis[v];
        }
        AC[now].end+=1;
    }
    
    inline void get_fail()
    {
        queue <int> Q;
        for(int i=0;i<=25;i++) // 第一层肯定没有之前的点与他匹配 
            if(AC[rt].vis[i]) 
                Q.push(AC[rt].vis[i]); // 因为默认是 0 所以没有写指向根节点 
        while(!Q.empty())
        {
            int u=Q.front(); Q.pop();
            for(int i=0;i<=25;i++) // 枚举所有子节点
            {
                if(AC[u].vis[i]) // 存在这个子节点
                {
                    AC[AC[u].vis[i]].fail=AC[AC[u].fail].vis[i];
                    // 子节点的fail指针指向当前节点的
                    // fail指针所指向的节点的相同子节点 
                    Q.push(AC[u].vis[i]);
                }
                else //不存在这个子节点
                AC[u].vis[i]=AC[AC[u].fail].vis[i];
                // 当前节点的这个子节点指向当
                // 前节点fail指针的这个子节点 
            }
        }
    }
    
    inline int query(char *ch)
    {
        int l=strlen(ch+1);
        int now=rt,ans=0,u;
        for(int i=1;i<=l;i++)
        {
            u=ch[i]-'a';
            now=AC[now].vis[u];
            for(int j=now; j && AC[j].end!=-1; j=AC[j].fail)
            {
                ans+=AC[j].end;
                AC[j].end=-1;
            }
        }
        return ans;
    }
    
    int main()
    {
        cin>>n;
        for(int i=1;i<=n;i++)
        {
            scanf("%s",ch+1);
            build(ch);
        }
        get_fail();
        scanf("%s",ch+1);
        printf("%d
    ",query(ch));
        return 0;
    }
    带注释
  • 相关阅读:
    第一章--linux基础
    深入浅出OOP(一): 多态和继承(早期绑定/编译时多态)
    LeetCode Letter Combinations of a Phone Number
    ios 仿android gallery控件
    android何如获取SIM卡提供国家代码(ISO)
    android 获取 imei号码
    overridePendingTransition的简介
    转 Android Activity之间动画完整版详解
    【android开发】使用PopupWindow实现页面点击顶部弹出下拉菜单
    Android 带你从源码的角度解析Scroller的滚动实现原理
  • 原文地址:https://www.cnblogs.com/zzzyc/p/8848954.html
Copyright © 2011-2022 走看看