首页 > 其他 > 详细

AC自动机

时间:2015-03-29 23:47:10      阅读:436      评论:0      收藏:0      [点我收藏+]

AC自动机

         直接学AC自动机比较难理解,强烈建议先学完KMP和字典树并进行一定的练习后,对于失配指针和字典树构造有一定理解后再来学AC自动机的内容。有关AC自动机的详细介绍可见刘汝佳的《算法竞赛入门经典训练指南》P214.

给你一个字典(包含n个不重复的单词),然后给你一串连续的字符串文本(长为len),问你该文本里面的哪些位置正好出现了字典中的某一个或某几个单词?输出这些位置以及出现的单词。

         这个问题可以用n个单词的n次KMP算法来做(效率为O(n*len*单词平均长度)),也可以用1个字典树去匹配文本串的每个字母位置来做(效率为O(len*每次字典树遍历的平均深度))。上面两种解法效率都不高,如果用AC自动机来解决的话,效率将为线性O(len)时间复杂度。(上述3种时间效率并未把构建KMP,字典树以及AC自动机的时间算上)

         KMP算法专门解决长文本的单模板匹配问题,字典树专门解决单个单词(短文本)多模板匹配问题。而AC自动机解决的是长文本的多模板匹配问题。且AC自动机不但时间上具有优势,空间上也颇具优势。

模板代码如下:

<span style="font-size:18px;">#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int maxnode=11000;
const int sigma_size=26;
struct AC_Automata
{
    int ch[maxnode][sigma_size];
    int val[maxnode];   // 每个字符串的结尾结点都有一个非0的val
    int f[maxnode];     // fail函数
    int last[maxnode];  // last[i]=j表j节点表示的单词是i节点单词的后缀,且j节点是单词节点
    int sz;

    //初始化0号根节点的相关信息
    void init()
    {
        sz=1;
        memset(ch[0],0,sizeof(ch[0]));
        val[0]=0;
    }

    //insert负责构造ch与val数组
    //插入字符串,v必须非0表示一个单词节点
    void insert(char *s,int v)
    {
        int n=strlen(s),u=0;
        for(int i=0; i<n; i++)
        {
            int id=s[i]-'a';
            if(ch[u][id]==0)
            {
                ch[u][id]=sz;
                memset(ch[sz],0,sizeof(ch[sz]));
                val[sz++]=0;
            }
            u=ch[u][id];
        }
        val[u]=v;
    }

    //getFail函数负责构造f和last数组
    void getFail()
    {
        queue<int> q;
        last[0]=f[0]=0;
        for(int i=0; i<sigma_size; i++)
        {
            int u=ch[0][i];
            if(u)
            {
                f[u]=last[u]=0;
                q.push(u);
            }
        }

        while(!q.empty())// 按BFS顺序计算fail
        {
            int r=q.front(); q.pop();
            for(int i=0; i<sigma_size; i++)
            {
                int u=ch[r][i];
                if(u==0)continue;
                q.push(u);

                int v=f[r];
                while(v && ch[v][i]==0) v=f[v];
                f[u]= ch[v][i];
                last[u] =  val[f[u]]?f[u]:last[f[u]];
            }
        }
    }

    //递归打印与结点i后缀相同的前缀节点编号
    //进入此函数前需保证val[i]>0
    void print(int i)
    {
        if(i)
        {
            printf("%d\n",i);
            print(last[i]);
        }
    }

    // 在s中找出 出现了哪几个模板单词
    void find(char *s)
    {
        int n=strlen(s),j=0;
        for(int i=0; i<n; i++)
        {
            int id=s[i]-'a';
            while(j && ch[j][id]==0) j=f[j];
            j=ch[j][id];
            if(val[j]) print(j);
            else if(last[j]) print(last[j]);
        }
    }

};
AC_Automata ac;</span>

AC自动机的基本应用

UVA 1449 Dominating Patterns(AC自动机):注意输入中有重复单词输入的情况。解题报告!

HDU 2222 Keywords Search(AC自动机):给你一个文本和多个单词,问你出现了多少个单词。注意单词可能会重复。解题报告

HDU 2896 病毒侵袭(AC自动机):AC自动机基本应用。解题报告!

HDU 3065 病毒侵袭持续中(AC自动机):AC自动机基本应用。解题报告

ZOJ 3430 Detect the Virus(AC自动机+字符串转换):本题麻烦在于匹配AC自动机之前需要先把64位编码转换为字符串。解题报告

ZOJ 3228 Searching the String(AC自动机):相同模板串不可重叠如何求最大出现次数?解题报告

 

AC自动机+DP

UVA 11468 Substring(AC自动机+概率DP):需要用改造后的AC自动机且注意概率dp的实现。解题报告!

POJ 2778 DNA Sequence(AC自动机+矩阵幂DP):需要先用DP求出递推公式,然后找出递推矩阵,本题就是一个矩阵幂运算的题。解题报告

HDU 2243 考研路茫茫——单词情结(AC自动机+矩阵幂):POJ2778的加强版。解题报告

POJ 1625 Censored!(AC自动机+DP):类似POJ2778,不过不需要矩阵幂。解题报告!

HDU 2825 Wireless Password(AC自动机+状态压缩DP):依然是生成字符串,但是这次要正好包含K个不同的模板单词。解题报告

HDU 2296 Ring(AC自动机+DP): 注意match数组的值。解题报告!

HDU 2457 DNA repair(AC自动机+DP):很巧妙的DP定义。解题报告!

HDU 3341 Lost‘s revenge(AC自动机+DP):另类的状态压缩DP题,以后记得这么压缩状态。解题报告!

 

 

AC自动机

原文:http://blog.csdn.net/u013480600/article/details/44732429

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!