• AC自动机


    AC自动机就是在trie树上的每一个节点上加了一个fail指针,用来多串匹配,类似fail数组。

    模板题:hdu2222 给出n个串,然后给一篇文章,问这n个串有多少个在文章里面出现过。

    首先我们把这n个串搞成一棵AC自动机,然后在这上面匹配这一篇文章。

    image

    (盗图一张)

    首先我们先建成一棵单纯的trie,然后每个结束节点做点标记啥的。

    然后我们来构造失败指针。我们在这颗trie上面bfs。比如我们现在要为一个新节点决定fail指针,如果这个节点上的字母为c,我们就沿着他父亲的失败指针走,直到走到一个节点,他的儿子中也有字母为c的节点。然后把当前节点的失败指针指向那个字母也为c的儿子。如果一直走到了root都没找到,那就把失败指针指向root。然后把这个节点加入队列,然后继续bfs它的孩子。然后如何把一段文字拿去匹配呢?我们开始在root,然后每次往下面匹配一个字符,匹配不到就往fail走。一直到走到某一个节点下面有这个字符了就往下面走,否则就走到root。然后我们把这个节点按照fail遍历一圈,所有这些子串都可以被匹配到。(注意此时不要改变当前节点)

    (如果窝讲不清楚这里似乎讲得比较清楚

    下面这份代码有一点实现上的技巧,如果一个点的next为空,它就会指向跳若干次fail之后的那个点…具体看代码

    #include <iostream>
    #include <stdio.h>
    #include <stdlib.h>
    #include <algorithm>
    #include <string.h>
    #include <vector>
    #include <limits>
    #include <set>
    #include <map>
    using namespace std;
    #define SZ 1000099
    int rot=1,ch[SZ][29],fail[SZ],cnt[SZ],e=1;
    void insert(char* s)
    {
        int cur=rot;
        for(int i=0;s[i];i++)
        {
            int c=s[i]-'a';
            if(!ch[cur][c]) ch[cur][c]=++e;
            cur=ch[cur][c];
        }
        cnt[cur]++;
    }
    int qs[SZ],h=0,t=0;
    void bfail()
    {
        h=t=0; fail[rot]=rot;
        for(int i=0;i<26;i++)
        {
            if(!ch[rot][i]) 
            {
                ch[rot][i]=rot; continue;
            }
            fail[ch[rot][i]]=rot;
            qs[t++]=ch[rot][i];
        }
        while(h!=t)
        {
            int cur=qs[h++];
            for(int c=0;c<26;c++)
            {
                if(!ch[cur][c]) ch[cur][c]=ch[fail[cur]][c];
                else
                {
                    fail[ch[cur][c]]=ch[fail[cur]][c];
                    qs[t++]=ch[cur][c];
                }
            }
        }
    }
    int match(char* s)
    {
        int cur=rot,ans=0;
        for(int i=0;s[i];i++)
        {
            int c=s[i]-'a'; cur=ch[cur][c];
            for(int f=cur;f!=rot;f=fail[f]) ans+=cnt[f], cnt[f]=0;
        }
        return ans;
    }
    int n,T;
    char str[SZ];
    int main()
    {
        scanf("%d",&T);
        while(T--)
        {
            rot=e=1;
            memset(ch,0,sizeof(ch));
            memset(fail,0,sizeof(fail));
            memset(cnt,0,sizeof(cnt));
            scanf("%d",&n);
            for(int i=1;i<=n;i++)
            {
                scanf("%s",str);
                insert(str);
            }
            bfail();
            scanf("%s",str);
            printf("%d
    ",match(str));
        }
    }

    然后有一个AC自动机的专题里面都是AC自动机有关的题目大家可以做一下:

    http://acm.hust.edu.cn/vjudge/contest/view.action?cid=25605#overview

    等窝做得差不多了再来贴代码。

    UPD:稍微做了几题,弃坑啦。

    image

    代码可以戳这里看到。(懒得拷上来了

    题解可以戳出这套题的神犇博客

  • 相关阅读:
    Java算法练习——整数反转
    Java算法练习—— Z 字形变换
    Java算法练习——最长回文子串
    vs code自动生成html代码
    thinkphp整合后台模板
    composer安装后台模板
    composer(作曲家)安装php-ml
    两个网站
    PHP的开源产品discuz
    onethink中的用户登录session签名
  • 原文地址:https://www.cnblogs.com/zzqsblog/p/5308865.html
Copyright © 2020-2023  润新知