AC自动机模板

来源:互联网 发布:串口助手软件下载 编辑:程序博客网 时间:2024/05/29 19:45

在串里找单词实际上是一个一个找,找到一个字符一样,加上他的count值,然后通过失败指针返回根节点,
所以要用一个temp来这样做,p的指针始终通过主串来改变,找到一个字符相等,p不变,temp变,再比较p
的儿子与主串中下一个字符,重复以上操作

#include<iostream>#include<queue>#include<cstring>#include<cstdio>#define maxn 1000005using namespace std;int head,tail;//队列头尾指针struct node{    node *fail; //fail指针表指向与与该元素相同的节点或根节点    node *next[26];//26个小写字母    int count;//记录每一个单词的末尾    node() //结构体重载    {        fail =NULL;        memset(next,0,sizeof(next));        count = 0 ;    }}*q[maxn];node *root;void insert(char s[])//建立字典树,这里就是26叉树{    node *p=root;    int len =strlen(s);    for(int i=0;i<len;i++)    {        int id=s[i]-'a';        if(p->next[id]==NULL) p->next[id]=new node();//若根节点下当前字符没有出现过,新建里一个当前字符的节点        p=p->next[id];//p指向当前节点    }    p->count++;//单词末尾的次数加1,代表右边出现过几个最后一个字符为末尾元素的子串}void build_ac()//建立fail指针{    node *p,*temp=NULL;    q[tail++]=root;//根节点先入队    while(head!=tail) //队列不为空时    {        p=q[head++];//出队        for(int i=0;i<26;i++)//26叉树,寻找单词的首字母        {            if(p->next[i])//不为空就表示该字符存在            {                if(p==root)//当孩子的父亲为根节点,也就是该字符位于第一层                    p->next[i]->fail=root; //直接将失败指针指向根节点                else                {                    temp = p->fail;//否则先记录当前节点的失败指针                    while(temp!=NULL)//当指向为空时,跳出,这里有个误区,不能在temp等于root时跳出,因为可能根节点的儿子中存在与当前节点儿子相同的字符                    {                       if(temp->next[i]) //如果当前节点指向的失败指针的儿子与当前节点的儿子相同                       {                           p->next[i]->fail=temp->next[i];//将当前节点的儿子的失败指针指向它                           break;                       }                       temp=temp->fail;//否则一直找与当前节点相同的元素                    }                    if(temp==NULL) p->next[i]->fail=root;//找不到,则直接把当前节点的儿子指向根节点                }                q[tail++]=p->next[i];//将当前节点的儿子入队以便在下一层完成他儿子的失败指针            }        }    }}int query(char s[])//查找有几个单词出现{    node *p=root,*temp;    int len = strlen(s);    int res=0;    for(int i=0;i<len;i++)    {        int id =s[i]-'a';        while(p->next[id]==NULL&&p!=root) p=p->fail;//如果p位于字典树一边的末尾,跳转到与p儿子相同元素的,若没有跳转到根节点        p=p->next[id];//p指向当前元素        if(p==NULL) p=root;//如果这样的字符不存在,则指向根节点        temp = p;//p不能改变,否则下一个字符就无法比较        while(temp!=root&&temp->count!=-1)//终点就是回到根而且再找的过程中找到一个必须标记为找过,否则会重复加        {            res+=temp->count;//加上当前字符的count值            temp->count=-1; //标记为访问过            temp=temp->fail; //不断找同的元素。直到回到根        }    }    return res;}


0 0
原创粉丝点击