题目大意:有N个由小写字母组成的模式串以及一个文本串T。每个模式串可能会在文本串中出现多次。你需要找出哪些模式串在文本串T中出现的次数最多。

对每个模式串建立一个Trie树。定义一个节点的Fail指针如下:如果节点x表示模式串a中字符a[i],x->Fail表示模式串b中字符b[j],则b[0,j]该前缀能在a[0,i]中找到与其相等的后缀。匹配时,沿trie树去匹配原串。如果trie树中当前节点cur->Next[i]==NULL,则说明当前所选择的匹配模式串不合适,由于前缀后缀的相等关系,令cur=cur->Fail转移到另一个模式串的前缀的最后一个字符表示的节点继续匹配。当前模式串的一个字符如果匹配成功,还要遍历一下cur的Fail,因为cur->Fail节点所表示的前缀可能便是整个字符串,这时便要将那些节点Sum++。

构造Fail指针方法:已知cur->Fail,设置cur->Next[i]->Fail。BFS遍历cur->Fail,如果Fail节点的Next[i]不为空,则将cur->Next[i]->Fail设为它,否则继续遍历cur->Fail->Fail,表示前缀后缀长度减小后能否匹配着。再不行就匹配到树根。

#include <cstdio>
#include <cstring>
#include <cassert>
#include <algorithm>
#include <cmath>
#include <queue>
using namespace std; #define Ord(c) c-'a'
const int MAX_NODE = 5e5 + , MAX_SLEN = 1e6 + , MAX_CHAR = , MAX_P = , MAX_PLEN = ; struct AC
{
char S[MAX_SLEN], P[MAX_P][MAX_PLEN];
int _pCnt; struct Node
{
int Sum;
int Cnt;
Node *Next[MAX_CHAR], *Fail;
Node():Sum(),Cnt(){}
}_nodes[MAX_NODE];
Node *Root, *Tail[MAX_P];
int _vCount; void Init(int pCnt)
{
Root = _nodes;
memset(_nodes, , sizeof(_nodes));
_pCnt = pCnt;
_vCount = ;
} Node *NewNode()
{
return _nodes + _vCount++;
} Node* BuildTrie(char *s)
{
int len = strlen(s);
Node *cur = Root;
for (int p = ; p < len; p++)
{
if (cur->Next[Ord(s[p])])
cur = cur->Next[Ord(s[p])];
else
cur = cur->Next[Ord(s[p])] = NewNode();
}
cur->Sum++;
return cur;
} void SetFail()
{
queue<Node*> q;
q.push(Root);
while (!q.empty())
{
Node *cur = q.front();
q.pop();
for (int i = ; i < MAX_CHAR; i++)
{
if (cur->Next[i])
{
Node *temp = cur->Fail;
while (temp)
{
if (temp->Next[i])
{
cur->Next[i]->Fail = temp->Next[i];
break;
}
temp = temp->Fail;
}
if (!temp)
cur->Next[i]->Fail = Root;
q.push(cur->Next[i]);
}
}
}
} void Find()
{
int len = strlen(S);
Node *cur = Root;
for (int p = ; p < len; p++)
{
while (cur != Root && !cur->Next[Ord(S[p])])
cur = cur->Fail;
if (!(cur = cur->Next[Ord(S[p])]))
cur = Root;
for (Node *temp = cur; temp != Root; temp = temp->Fail)
if (temp->Sum)
temp->Cnt++;
}
} void Proceed()
{
for (int i = ; i < _pCnt; i++)
Tail[i] = BuildTrie(P[i]);
SetFail();
Find();
}
}g; int main()
{
#ifdef _DEBUG
freopen("c:\\noi\\source\\input.txt", "r", stdin);
#endif
int pCnt;
while (scanf("%d", &pCnt) && pCnt)
{
g.Init(pCnt);
for (int i = ; i < pCnt; i++)
scanf("%s", g.P[i]);
scanf("%s", g.S);
g.Proceed();
int ans = ;
for (int i = ; i < pCnt; i++)
ans = max(ans, g.Tail[i]->Cnt);
printf("%d\n", ans);
for (int i = ; i < pCnt; i++)
if (g.Tail[i]->Cnt == ans)
printf("%s\n", g.P[i]);
}
return ;
}

luogu3769 【模板】AC自动机(加强版)的更多相关文章

  1. luoguP3796[模板]AC自动机(加强版)

    传送门 ac自动机模板,可能我写的ac自动机是有点问题的,所以跑的有些慢 暴力跳fail统计 代码: #include<cstdio> #include<iostream> # ...

  2. luoguP3808[模板]AC自动机(简单版)

    传送门 ac自动机模板题,裸的多串匹配 代码: #include<cstdio> #include<iostream> #include<algorithm> #i ...

  3. 算法模板——AC自动机

    实现功能——输入N,M,提供一个共计N个单词的词典,然后在最后输入的M个字符串中进行多串匹配(关于AC自动机算法,此处不再赘述,详见:Aho-Corasick 多模式匹配算法.AC自动机详解.考虑到有 ...

  4. 模板 AC自动机

    题目描述 有$N$ 个由小写字母组成的模式串以及一个文本串$T$ .每个模式串可能会在文本串中出现多次.你需要找出哪些模式串在文本串$T$ 中出现的次数最多. 输入输出格式 输入格式: 输入含多组数据 ...

  5. 算法竞赛模板 AC自动机

    AC自动机基本操作 (1) 在AC自动机中,我们首先将每一个模式串插入到Trie树中去,建立一棵Trie树,然后构建fail指针. (2) fail指针,是穿插在Trie树中各个结点之间的指针,顾名思 ...

  6. [模板][P3796]AC自动机(加强版)

    Description: 输出有哪些模式串在文本串中出现次数最多,这个次数是多少 Hint: 多组数据,$ len_{文本串}<=10^6,\sum len_{模式串} <= 70*150 ...

  7. 【模板】AC自动机加强版

    题目大意:给定 N 个模式串和一个文本串,求每个模式串在文本串中出现的次数. 题解:文本串在自动机上匹配的过程中,记录下自动机上每一个状态被访问的次数.对于访问到的节点 i,则状态 i 的后缀中存在的 ...

  8. 洛谷.3808/3796.[模板]AC自动机

    题目链接:简单版,增强版 简单版: #include <cstdio> #include <cstring> const int N=1e6+5,S=26; char s[N] ...

  9. 模板—AC自动机

    #include<iostream> #include<cstdio> #include<cstring> using namespace std; struct ...

  10. 模板——AC自动机

    传送门:QAQQAQ 定义nxt[u]=v表示从u开始不断沿着失配边跳到的第一个是标记点的端点v,那么我们再匹配时沿着last跳,每跳到一个last,它就一定对应一个模式串,所以效率是非常高的. 和K ...

随机推荐

  1. Spring Boot (16) logback和access日志

    Spring Boot 内部采用的是Commons Logging进行日志记录,但是在底层为Java Util Logging.Log4J2.Logback等日志框架提供了默认配置. logback ...

  2. SQLServer 在存储过程里使用事务控制的简单小例子

    alter proc sp_test(     @name varchar(50))asbegin    --开始事务   begin transaction   --设置一个存储报错代码的变量   ...

  3. (转)Arcgis for JS之地图自适应调整

    http://blog.csdn.net/gisshixisheng/article/details/42675897 概述:本节讲述的内容为当浏览器大小发生变化或者地图展示区域的大小发生变化时,地图 ...

  4. 文件元数据、文件夹操作(day08)

    一.获取文件的元数据(meta data) 通过read write可以对文件的内容进行读写. 但是今天我们要操作的是文件的元数据(文件的属性信息) day08$ls -l hello -rw-rw- ...

  5. BZOJ1001 BJOI2006 狼抓兔子

    Description 现在小朋友们最喜欢的"喜羊羊与灰太狼",话说灰太狼抓羊不到,但抓兔子还是比较在行的,而且现在的兔子还比较笨,它们只有两个窝,现在你做为狼王,面对下面这样一个 ...

  6. 设置随机 User-Agent

    本文转载自以下网站: Scrapy 中设置随机 User-Agent 的方法汇总 https://www.makcyun.top/web_scraping_withpython14.html 一行代码 ...

  7. 06.系统编程-4.多线程和GIL

    为什么有人会说 Python? 多线程是鸡肋?知乎上有人提出这样一个问题,在我们常识中,多进程.多线程都是通过并发的方式充分利用硬件资源提高程序的运行效率,怎么在 Python 中反而成了鸡肋? 有同 ...

  8. Python-Pandas简单操作

    1.直接构建复杂嵌套索引 2. MultiIndex方式构建复杂的索引 多层索引操作 pandas堆叠处理

  9. 第一个go程序和基本语法

    目录 第一个go程序和基本语法 一. 第一个go程序 二. 基础语法 1. 命名 2. 变量 3 常量与枚举 4. 数据类型 5. fmt包的使用 6. 类型别名 7. 类型转换 8. 运算符 第一个 ...

  10. 5、Linux的常用命令

    ls 查看当面目录结构 ls -l 列表查看当前目录 cd:切换目录 pwd:显示目前的目录 mkdir:创建一个新的目录 rmdir:删除一个空的目录 cp: 复制文件或目录 rm: 移除文件或目录 ...