AC自动机模板

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

#include<iostream>
#include<queue>
#include<cstring>
#include<cstdio>
#define maxn 1000005
using 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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值