ac自动机

来源:互联网 发布:java api全部包类 编辑:程序博客网 时间:2024/06/09 11:08

预备知识:kmp算法,字典树
debug了好多天,才终于出正确结果了,终于知道逻辑错误的可怕了,越来越理解什么叫做“比编译错误更折磨人的是逻辑错误”。
ps.学前感觉很难,理解了才感觉自动机这个东西真是很奇妙。
具体实现就看注释了。。。。。。

#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;}
原创粉丝点击