ac自动机

预备知识:kmp算法,字典树
debug了好多天,才终于出正确结果了,终于知道逻辑错误的可怕了,越来越理解什么叫做“比编译错误更折磨人的是逻辑错误”。

具体实现就看注释了。。。。。。

#include<cstdio>
#include<string>
#include<queue>
#include<iostream>
using namespace std;
struct node
{
    char c;//该节点所代表的值,其实不加也可以,因为字典树的每一层就是a-z的字典序
    int end;//是否为某个单词的结尾
    int num;//以该节点,为结尾的单词个数
    node *n[26];//字典树指针
    node *fail;//fail指针
    char *str;//若该节点是单词结尾,要在此处存入单词,输出统计结果时用
    node()//初始化节点
    {
        end = 0;
        num = 0;
        fail = NULL;
        for (int i = 0; i < 26; i++)
        {
            n[i] = NULL;
        }
    }
};
queue<node*> endnode;//单词结尾的节点组成的队列,用于最后输出统计结果
void insert(node *root, char *c)//字典树中插入字符串
{
    node *op = root;//将要插入的节点的父节点指针
    node *p;//新建节点时使用的操做指针
    int x;//存储字符在子结点的下标
    for (int i = 0; i < strlen(c); i++)
    {
        x = c[i] - 'a';
        if (op->n[x] == NULL)//若没有该节点,则分配空间新建节点
        {
            p = (node *)malloc(sizeof(node));
            //对新节点赋值
            p->fail = NULL;
            p->end = 0;
            p->c = c[i];
            p->num = 0;
            //子节点指空
            for (int op = 0; op < 26; op++)
            {
                p->n[op] = NULL;
            }
            op->n[x] = p;//将新节点连入字典树
        }
        if (i == strlen(c) - 1)//当处理到字符串最后一位时,将以此位结尾的单词数目加一
        {
            op->n[x]->end++;
            op->n[x]->str = c;
            endnode.push(op->n[x]);
        }
        op = op->n[x];//为处理下一个做准备
    }
}
void build_fail(node *root)//建立fail指针,利用队列(FIFO),对字典树广度优先搜索
{
    node *op,*findfail;
    //op为要建立fail指针的节点的父指针
    //findfail永远指向可能是要找的节点的父节点
    queue<node*> k;
    op = root;
    //k.push(op);
    //对root下的第一层做特殊处理,fail指向root
    for (int i = 0; i < 26; i++)
    {
        if (op->n[i] != NULL)
        {
            op->n[i]->fail = root;
            k.push(op->n[i]);
        }
    }
    while (!k.empty())
    {
        op = k.front();//先提取元素,再出队列
        k.pop();
        findfail = op->fail;
        for (int i = 0; i < 26; i++)
        {
            if (op->n[i] != NULL)
            {
                k.push(op->n[i]);//新元素入队
                if (findfail->n[i] != NULL)
                {
                    op->n[i]->fail = findfail->n[i];//op.fail.n[i]存在,则直接建立fail指针
                }
                else//fail指针不存在
                {
                    //当findfail追到第二层时,该节点的n[i].fail就是root了。
                    if (findfail->fail = root)
                        op->n[i]->fail = root;
                    else//让findfail一级一级找它的fail指针,当然提前要做判断
                        findfail = findfail->fail;
                }
            }
        }
    }
}
void ac_automachine(node *root,char *c)
{
    node *op=root;
    for (int i = 0; i < strlen(c); i++)//对文本处理
    {
        int x = c[i] - 'a';
        if (op->n[x] != NULL)
        {
            if (op->n[x]->end > 0)
            {
                op->n[x]->num++;//判断下是否是单词结尾,是,就++
            }
            op = op->n[x];
            continue;
        }
        else//当不存在时就跳转到fail指针所指向的位置
        {
            while (op != root)//到root就可以截至
            {
                op = op->fail;//沿着fail指针一直延续下去
                if (op->n[x] != NULL)
                {
                    if (op->end > 0) op->num++;
                    op = op->n[x];
                    if (op->end > 0) op->num++;
                    break;
                }
            }
        }
    }
}
void output()
{
    while (!endnode.empty())
    {
        node *op = endnode.front();
        endnode.pop();
        printf("%s的个数:%d\n", op->str, op->num);
    }

}
int main()
{
    node a;
    a.c = '&';
    char c[100] = "sayhgbrherbrbrbrhbrh";
    //scanf("%s", c);
    insert(&a, "say");
    insert(&a, "brh");
    insert(&a, "she");
    insert(&a, "her");
    insert(&a, "he");
    build_fail(&a);
    ac_automachine(&a, c);
    output();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值