AC自动机的学习

花了一天把AC最基础的东西弄明白了,想弄清楚AC自动机就得学会KMP和字典树,因为这个算法是这个两个算法的基础上建立的。我们得明白字典树如何建立的和得深度理解KMP中next数组得含义。AC自动机中的fail指针和KMP中的next数组有异曲同工之处。fail指针的作用:在失配时能够跳转到合适的位置继续匹配。

AC自动机自己也是学习了基础,博客肯定时写不出来的。。。。。

参考博客:

AC自动机:http://www.cppblog.com/menjitianya/archive/2014/07/10/207604.html

fail指针的理解:https://blog.csdn.net/u013371163/article/details/60469145(我感觉很形象,很容易理解)

AC自动机的模板题 :HDU2222

附上蒟蒻的代码(有注释,可能有些地方理解的不对Orz)

///#include<bits/stdc++.h>
///#include<unordered_map>
///#include<unordered_set>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
#include<bitset>
#include<set>
#include<stack>
#include<map>
#include<list>
#include<new>
#include<vector>
#define MT(a,b) memset(a,b,sizeof(a));
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double pai=acos(-1.0);
const double E=2.718281828459;
const int MAXN=1e5;
const ll mod=1e9+7;
const int INF=0x3f3f3f3f;

struct node
{
    node *child[26];        ///儿子
    node *fail;             ///fail指针
    int cnt;                ///计数
};
node *root;                 ///根节点

char word[55];              ///单词
char text[1000005];         ///文本

void insert_word(char *s)   ///建字典树
{
    node *now=root;
    int i=0,x;
    while(s[i])
    {
        x=s[i]-'a';
        if(now->child[x]==NULL)     ///如果没有儿子
        {
            node *temp=new node();     ///申请空间
            temp->cnt=0;               ///初始化
            temp->fail=NULL;
            memset(temp->child,NULL,sizeof(temp->child));

            now->child[x]=temp;
        }
        now=now->child[x];
        i++;
    }
    now->cnt++;         ///叶子节点计数
}

void build_fail()          ///用父亲去更新儿子的fail
{
    node *now,*temp;
    queue<node *>q;
    q.push(root);           ///root先入队
    while(!q.empty())
    {
        now=q.front();
        q.pop();
        for(int i=0;i<=25;i++)
        {
            if(now->child[i]!=NULL)
            {
                if(now==root)           ///如果当前为根,他的儿子的fail都指向root
                    now->child[i]->fail=root;
                else
                {
                    temp=now->fail;     ///定义一个变量用来转移当前的寻找状态
                    while(temp!=NULL)   ///如果找到root的fail就寻找结束了
                    {
                        if(temp->child[i]!=NULL)        ///如果找到了满足条件的fail
                        {
                            now->child[i]->fail=temp->child[i];     ///更新儿子的fail
                            break;      ///跳出寻找
                        }
                        temp=temp->fail;        ///继续寻找
                    }
                    if(temp==NULL)              ///如果找到了root的fail
                        now->child[i]->fail=root;   ///儿子的fail指向root
                }
                q.push(now->child[i]);          ///儿子入队
            }
        }
    }
}

int ac_auto(char *s)
{
    node *now=root,*temp;
    int i=0,x,ans=0;
    while(s[i])
    {
        x=s[i]-'a';
        ///如果找不到这个字母,利用fail指针一直找
        ///如果找到这个字母就结束
        ///如果当前为root节点,就不需要寻找
        while(now!=root&&now->child[x]==NULL)
            now=now->fail;

        ///这一步针对是对root的判断
        now=now->child[x];
        if(now==NULL)       ///如果root没有这个儿子,则回到root
            now=root;

        temp=now;           ///定义临时变量去寻找答案
        while(temp!=root&&temp->cnt>=0)     ///如果当前不是root,并且这个地方没有来过
        {
            ans+=temp->cnt;             ///cnt==0,不影响答案
            temp->cnt=-1;               ///标记这个地方来过
            temp=temp->fail;            ///继续寻找
        }
        i++;
    }
    return ans;
}

void init()     ///初始化
{
    root->fail=NULL;    ///根的fail一定是空
    root->cnt=0;
    memset(root->child,NULL,sizeof(root->child));   ///清空儿子
}

int main()
{
    int t,n;
    root=new node();    ///根节点申请空间
    scanf("%d",&t);
    while(t--)
    {
        init();         ///初始化
        scanf("%d",&n);
        while(n--)
        {
            scanf("%s",word);
            insert_word(word);      ///插入单词
        }
        build_fail();               ///构建失败指针
        scanf("%s",text);
        printf("%d\n",ac_auto(text));       ///寻找
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_42211531/article/details/86596468
今日推荐