• 蒟蒻林荫小复习——AC自动机


    AC自动机:Aho-Corasick automaton,该算法在1975年产生于贝尔实验室,是著名的多模匹配算法。

    今天蒟蒻林荫来复习AC自动机

    前置芝士

    1. KMP算法
    2. TRIE树(这个不会的话出门右转幼儿园)

    好吧我承认AC自动机要比KMP好理解。

    如何建立一个AC自动机

    1. 建立一颗字典树,其中插入操作和普通字典树的一模一样
    2. 在字典树上构造fair数组(这也就是AC自动机与KMP联系起来的地方)
    3. 开始查找

    如何构造一个fair数组?/fair数组是个啥?

    fair数组与KMP中的next一样都是作为失配数组存在的,而对于节点X,fair所存的元素是fair[x的父亲]的一个与X相同的儿子,如果没有fair指向root。

    也就是说fair[x]=某个与x节点相同的点或者root,并且fair[x]所在的链一定存在于X所在的这条链上,换句话说,fair[x]所跳转到的字符串一定是X所在串从X向前的一部分,且一定包括X。

    至于如何求fair,就通过BFS来实现

    下面来张图

    1. 红色线:root出队,将h和s两个点fair置为root,并将h和s入队
    2. 蓝色线:h出队,子节点e的fair等于自己父亲的fair的子节点中e的位置,不存在所以为root,e入队,同时s出队,子节点a的fair为自己父亲的fair的子节点中a的位置不存在为root,a入队,子节点h的fair为自己父亲fair的子节点中h的位置,存在,fair[tire[now][i]]=tire[fair[now]][i];h入队
    3. 绿色线:e出队,子节点r的fair等于自己父亲的fair(root)中子节点中r的位置,不存在为root,r入队,a出队,子节点y的fair同样为root,y入队,h出队,子节点e的fair等于父亲的fair的子节点中e的位置,fair[tire[now][i]]=tire[fair[now]][i],与左子树的e相连,e入队,子节点r的fair为root,入队;

    这样的话fair数组就整理好了,对于不存在的点,tire[now][i]=tire[fair[now]][i]。如果这个点不存在,那么就指向自己父亲的fair的对应位置。类似于一个状态压缩,如果当前x的fair中并没有与x相同的元素,那么就前往fair[x]的fair,因为fair[x]代表的元素==x,fair[fair[x]]代表的元素==fair[x]代表的元素

    void getfail()
    {
        queue<int>q;
        for(int i=0;i<26;i++)
        {
            if(trie[0][i])
            {
                q.push(trie[0][i]);
                fail[trie[0][i]]=0;
            }
        }
        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];
                }
            }
        }
    }
    FAIR

    万恶的fair终于整理完成了

    下面来说说查询

    AC自动机是用来查询原串中出现模式串次数的,所以这里引入一个变量tdword[x]代表在x节点结尾的模式串的个数

    那么查询的第一重循环一定是在tire上确定原串每一位的位置的,联想到刚才fair的定义,也就是说一个点如果目前与原串匹配,那么这个点的fair及fair以上字符构成的串也一定能和原串匹配,所以说每到达一个节点,都要访问这个点可以通过fair数组所访问到的每一个点

    int query(string x)
    {
        int ans=0;
        int now=0;
        for(int i=0;i<x.size();i++)
        {
            now=trie[now][x[i]-'a'];for(int j=now;j&&tdword[j]!=-1;j=fail[j])
            {
                ans+=tdword[j]; 
                tdword[j]=-1;
            }
        }
        return ans;
    }

    而本人的代码对应的是洛谷上AC自动机的模板,该题要求统计出现模式串的种类数,第二重循环即为根据某个点跳fair,如果tdword[j]==-1的话就代表这个点已经在之前被访问过一遍,那么它的fair肯定也全部都跳完了。所以说跳到tdword[j]==-1的情况就停下来。

    OK,完结撒花!

  • 相关阅读:
    关于body添加position:fixed会返回到网页顶部的问题
    微信个人信息的接口
    无缝滚动+定时器
    判断各种浏览器的内核和 移动端的类型
    laery.js提示层结束之后自动跳转到新页面
    支持火狐的文本超出隐藏以省略号显示
    多个列表同时进行倒计时
    记录制作人生第一把分体式键盘 ^.^
    NoSQLBooster for mac 破解(win同理)
    手机对应归属地规则
  • 原文地址:https://www.cnblogs.com/XLINYIN/p/11355527.html
Copyright © 2020-2023  润新知