2018/1/30训练日记 (AC自动机进一步理解)

今天上午跟他们又讨论了讨论AC自动机的实现模板,感觉理解更深刻了

对于用数组来实现AC自动机的模板

f函数就相当于fail指针,last函数我们感觉实际上对于fail指针的优化,或者说整个AC自动机的优化

因为在利用指针实现AC自动机的模板去求fail函数实际上是没有出现last函数这个东西,昨天主要是看了利用指针

去实现,今天上午看的数组模板。

f【】数组指向的实际上就是 跟当前结点的单词的最长后缀的前缀的那个结点

而last【】数组则类似于进一步优化

他实际上是指的跟当前结点的单词的最长后缀的前缀的那个结点(如果是一个单词结点的话,那么它实际上是和f[]那个结点相同的)  而如果它不是一个单词结点的话,那个它就沿着f[]函数的那个点继续去找   直到找到一个是前缀的单词结点 如果没有的话则直接指向根节点。

什么意思呢,last【】数组的那个点的表示实际上就是离当前结点的单词的最长后缀的前缀最近的那个单词结点。

(可能会有点绕,但应该是这个意思没错了)

下午和晚上就一直在看例题,一般基础应用就是套模板之后再去改模板,但AC自动机加上dp之后感觉难了好多QAQ

#include
#include
#include
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         {
            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 q;
        last[0]=f[0]=0;
        for(int i=0; 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             {
                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         {
            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;

你可能感兴趣的:(AC自动机练习,2018年寒假训练日记)