hdu 2222 AC自动机(模板题)

<题目链接>

题目大意:

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

解题分析:

这是多模匹配问题,如果用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

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

时间: 2024-08-02 05:29:48

hdu 2222 AC自动机(模板题)的相关文章

HDU 2222 AC自动机模板题

题目: http://acm.hdu.edu.cn/showproblem.php?pid=2222 AC自动机模板题 1 #include<stdio.h> 2 #include<string.h> 3 #include<queue> 4 using namespace std; 5 char key[55]; 6 char des[1111111]; 7 struct node{ 8 node *fail; 9 node *next[26]; 10 int cnt;

HDU 3065 (AC自动机模板题)

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=3065 题目大意:多个模式串,范围是大写字母.匹配串的字符范围是(0~127).问匹配串中含有哪几种模式串,且每种模式串出现了多少次. 解题思路: AC自动机模板题.模式串的范围是大写字母,但是匹配串的范围却是(0~127). 如果Trie 开到 128 加上不回收内存,就会MLE. 实际上开到26就行了,find的时候对于c<0||c>26,强制令pos=root出现失配,并开始下一个字符就行了

HDU 2896 (AC自动机模板题)

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=2896 题目大意:多个模式串.多个匹配串.其中串的字符范围是(0~127).问匹配串中含有哪几个模式串. 解题思路: AC自动机模板题.注意一下字符范围. cnt记录这个模式串的个数改为这个模式串的index. find的时候,把找到的index压入vector里面即可. 注意有多个匹配串,每次find之后会把last->cnt修改,原因是防止一个模式串出现了多次被压入vector,所以先备份一下,

HDU 2222 AC自动机(模版题)

Keywords Search Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)Total Submission(s): 70290    Accepted Submission(s): 23917 Problem Description In the modern time, Search engine came into the life of everybody lik

HDU 2222(AC自动机模板题)

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=2222 题目大意:多个模式串.问匹配串中含有多少个模式串.注意模式串有重复,所以要累计重复结果. 解题思路: AC自动机模板题. 一开始使用LRJ的坑爹静态模板,不支持重复的模式串. 在做AC自动机+DP的时候,扒了zcwwzdjn大神的动态优化(失配指向root)写法,以及借鉴了网上的AC自动机模板, 搞出了这么一个支持重复串的模板. #include "cstdio" #include

HDU 2222 Keywords Search(AC自动机模板题)

原题大意:原题链接 先给定T个单词,然后给定一个字符串,查询该字符串中包含多少个给定的单词 解题思路:AC自动机模板题 参考链接:哔哩哔哩算法讲堂 WA版本 注意:因为输入的单词可能有重复,那么Insert()函数中p->id=id;语句中p->id会被覆盖,在Query()函数中会一次性全部被清零,导致不能查询重复单词,以至于结果res错误. #include<queue> #include<cstdio> #include<cstring> using

hdu5384 AC自动机模板题,统计模式串在给定串中出现的个数

http://acm.hdu.edu.cn/showproblem.php?pid=5384 Problem Description Danganronpa is a video game franchise created and developed by Spike Chunsoft, the series' name is compounded from the Japanese words for "bullet" (dangan) and "refutation&q

LA 4670 出现次数最多的子串 (AC自动机模板题)

Dominating Patterns Time Limit:3000MS   Memory Limit:Unknown   64bit IO Format:%lld & %llu [Submit]  [Go Back]  [Status] Description The archaeologists are going to decipher a very mysterious ``language". Now, they know many language patterns; ea

NYOJ 1085 数单词 (AC自动机模板题)

数单词 时间限制:1000 ms  |  内存限制:65535 KB 难度:4 描述 为了能够顺利通过英语四六级考试,现在大家每天早上都会早起读英语. LYH本来以为自己在6月份的考试中可以通过六级,可是没想到,成绩出来以后,居然没有通过.所以他不得不付出更多的时间来学习英语. 要想通过六级,最基本的要求就是词汇量.为了能够更快的记住一些陌生单词,LYH有时会找一些英语文章来读. 今天早上,LYH又找了一篇文章.读之前,他突然萌生出一个想法:文章中哪些单词出现的次数最多呢? 输入 第一行输入一个