AC自动机入门——(hdu-2222)

AC自动机

以前还真认为AC自动机就是自动AC机
AC自动机,就是说,多模式匹配串,更像是一种树上KMP,在学这个之前有一些前置技能:KMP字典树
想学AC自动机,字典树是必须要会的,因为它就是在字典树上进行操作,但是它比字典树多了一个失配指针(fail指针)。本来我们要对一些的串进行匹配,那么我们暴力要一个一个去匹配,每个串都要从头开始匹配,那样的话,太浪费时间了,但是加上了fail指针之后,就直接开始一遍过就可以匹配所有的模式串(其实我感觉,AC自动机的fail指针就像是KMP的next[]数组在当前算法一样的重要地位)理解了fail指针,AC自动机也就理解了。
AC自动机三步走
1.
用字典树建图,对模式串建图,用字典树把模式串全部存起来。
2.
构建图中的fail指针。
1).用BFS进行进行层次遍历,先把root节点压入队列。(root的节点的fail为0)
2).对队列里面的结点进行出队操作,然后以次遍历这一层26个字母,如果有在树上的,那么我们通过它的父亲的fail节点,来判断当前节点的fail指针。
如果它的父亲节点的fail指针指向root节点,那么儿子的fail指针也将指向根节点。
如果它的父亲节点fail指针不为0,那么就找它指向的那个节点,判断它的树下有没有当前节点的字母存在,存在的话,当前节点的fail指针就指向这个字母的编号,不存在就一直向上找,一直找到存在的或者root节点,然后退出循环。
3).压入队列。
4).若不存在这个字母节点,那么主串就直接跟着父节点的fail指针往下继续找。
3.
主串查询操作
1).按串开始按位遍历。
2).每找到一个数,先判断这个数是否存在,要么是root节点,不然就跟着fail指针往上找。
3).找到符合要求的之后,判断当前节点是否作为单词的根节点,有的话则num+=tree[x].numm(当前这个节点的有多少个单词以此结尾)。
HDU—2222 题目传送门
题目大意
给你n个字符串,让再给你一条很长的主串,问这n个字符串中有多少个在这个主串里出现过。
思路
AC自动机入门模板题,

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<algorithm>
#include<queue>
using namespace std;
const int N=1e6+10;
struct zxc
{
    int fail;
    int cz;
    int a[30];
}tree[3*N];
char sl[3*N];
char s[55];
int num;
void init(int x)
{
    for(int i=0;i<=26;i++)
    {
        tree[x].a[i]=-1;
    }
    tree[x].fail=-1;
    tree[x].cz=0;
}
void build(char z[])
{
    int u=0;
    int l=strlen(z);
    for(int i=0;i<l;i++)
    {
        int c=z[i]-'a';
        if(tree[u].a[c]==-1)
        {
            tree[u].a[c]=num++;
            u=tree[u].a[c];
            init(u);
        }
        else
        {
            u=tree[u].a[c];
        }

    }
   tree[u].cz++;
}
void ACmadefail()
{
    queue<int>q;
    tree[0].fail=-1;
    q.push(0);
    while(!q.empty())
    {
        int p;
        int u=q.front();
        q.pop();
        for(int i=0;i<26;i++)
        {
            int v=tree[u].a[i];
            if(v>=0)
            {
                if(u==0)
                {
                    tree[v].fail=0;
                }
                else
                {
                    p=tree[u].fail;
                    while(p>=0)
                    {
                        if(tree[p].a[i]>=0)
                        {
                            tree[v].fail=tree[p].a[i];
                            break;
                        }
                        p=tree[p].fail;
                    }
                    if(p<0)
                    {
                        tree[v].fail=0;
                    }
                }
                q.push(v);
            }
            else
            {
                tree[u].a[i]=tree[tree[u].fail].a[i];
            }
        }
    }
}
int ACslary()
{
    int num=0;
    int p=0,x;
    int i=0;
    while(sl[i])
    {
        int j=sl[i]-'a';
        while(tree[p].a[j]<=0&&p)p=tree[p].fail;
        p=tree[p].a[j];
        if(p<=0)p=0;
        x=p;
        while(x&&tree[x].cz!=0)
        {
            num+=tree[x].cz;
            tree[x].cz=0;
            x=tree[x].fail;
        }
        i++;
    }
    return num;
}
int n;
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {

        init(0);
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%s",s);
            build(s);
        }
        scanf("%s",sl);
        ACmadefail();
        printf("%d\n",ACslary());
    }
    return 0;
}


发布了42 篇原创文章 · 获赞 5 · 访问量 928

猜你喜欢

转载自blog.csdn.net/qq_43402296/article/details/103915521