码迷,mamicode.com
首页 > 其他好文 > 详细

hdu 2222 AC自动机(模板题)

时间:2018-08-05 21:27:15      阅读:146      评论:0      收藏:0      [点我收藏+]

标签:uil   code   队列   循环   next   移动   形式   scan   构造   

<题目链接>

题目大意:

给你一些单词,和一个字符串,问你这个字符串中含有多少个上面的单词。

解题分析:

这是多模匹配问题,如果用KMP的话,对每一个单词,都跑一遍KMP,那么当单词数量非常多的时候,耗时会非常多,所以这里用到了AC自动机,这是一种类似于Trie树的数据结构,但是同时,它也用到了KMP算法中 next数组的思想。

下面是AC自动机指针形式的题解:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct Node
{
    int cnt;//是否为该单词的最后一个结点 
    Node *fail;//失败指针 
    Node *next[26];//Trie中每个结点的各个节点 

    void Init()
    {
        cnt = 0;
        fail = NULL;
        for (int i = 0; i<26; i++)
            next[i] = NULL;
    }

}*queue[500005];//队列,方便用BFS构造失败指针 
char s[1000005];//主字符串 
char keyword[55];//需要查找的单词 
Node *root;//头结点 



void Build_trie(char *keyword)//构建Trie树 
{
    Node *p, *q;
    int i, v;
    int len = strlen(keyword);
    for (i = 0, p = root; i<len; i++)
    {
        v = keyword[i] - a;
        if (p->next[v] == NULL)
        {
            q = (struct Node *)malloc(sizeof(Node));
            q->Init();
            p->next[v] = q;//结点链接 
        }
        p = p->next[v];//指针移动到下一个结点 
    }
    p->cnt++;//单词最后一个结点cnt++,代表一个单词 
}

void Build_AC_automation(Node *root)          //利用bfs,建立每个节点的fail指针
{
    int head = 0, tail = 0;//队列头、尾指针 
    queue[tail++] = root;//先将root入队 
    while (head < tail)
    {
        Node *p = NULL;
        Node *temp = queue[head++];//弹出队头结点 
        for (int i = 0; i<26; i++)   //接下来,为temp的每一个儿子节点找相对应的fail指针
        {
            if (temp->next[i] != NULL)//找到实际存在的字符结点 
            { //temp->next[i] 为该结点,temp为其父结点 
                if (temp == root)//若是第一层中的字符结点,则把该结点的失败指针指向root 
                    temp->next[i]->fail = root;
                else
                {
                    //依次回溯该节点的父节点的失败指针直到某节点的next[i]与该节点相同,
                    //则把该节点的失败指针指向该next[i]节点; 
                    //若回溯到 root 都没有找到,则该节点的失败指针指向 root
                    p = temp->fail;//将该结点的父结点的失败指针给p 
                    while (p != NULL)
                    {
                        if (p->next[i] != NULL)
                        {
                            temp->next[i]->fail = p->next[i];
                            break;
                        }
                        p = p->fail;
                    }
                    //让该结点的失败指针也指向root 
                    if (p == NULL)                        
                        temp->next[i]->fail = root;
                }
                queue[tail++] = temp->next[i];//每处理一个结点,都让该结点的所有孩子依次入队 
            }
        }
    }
}

int query(Node *root)
{ //i为主串指针,p为模式串指针 
    int i, v, count = 0;
    Node *p = root;
    int len = strlen(s);
    for (i = 0; i<len; i++)
    {
        v = s[i] - a;
        //由失败指针回溯查找,判断s[i]是否存在于Trie树中 
        while (p->next[v] == NULL && p != root)
            p = p->fail;
        p = p->next[v];//找到后p指针指向该结点 
        if (p == NULL)//若指针返回为空,则没有找到与之匹配的字符 
            p = root;

        
        Node *temp = p;//匹配该结点后,沿其失败指针回溯,判断其它结点是否匹配 
        while (temp != root)//匹配结束控制 
        {
            if (temp->cnt >= 0)//判断该结点是否被访问 
            {
                count += temp->cnt;//由于cnt初始化为 0,所以只有cnt>0时才统计了单词的个数 
                temp->cnt = -1;//标记已访问过 
            }
            else//结点已访问,退出循环 
                break;
            temp = temp->fail;//回溯 失败指针 继续寻找下一个满足条件的结点 
        }
    }
    return count;
}

int main()
{
    int T, n;
    scanf("%d", &T);
    while (T--)
    {
        root = (struct Node *)malloc(sizeof(Node));
        root->Init();
        scanf("%d", &n);
        for (int i = 0; i<n; i++)
        {
            scanf("\n%s", keyword);
            Build_trie(keyword);
        }
        Build_AC_automation(root);
        scanf("\n%s", s);
        printf("%d\n", query(root));
    }
    return 0;
}

 

2018-08-05

hdu 2222 AC自动机(模板题)

标签:uil   code   队列   循环   next   移动   形式   scan   构造   

原文地址:https://www.cnblogs.com/00isok/p/9426990.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!