题目大意:有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. SQLServer 使用变量动态行转列

    drop table #testcreate table #test(    id int identity(1,1) primary key,    bizDate varchar(50),    ...

  2. [转]JAVA回调机制(CallBack)详解

    看见一篇博客比较通俗的解释了回调机制,转载一下,感谢原文作者Bro__超,原文地址:http://www.cnblogs.com/heshuchao/p/5376298.html 序言 最近学习jav ...

  3. Android贝塞尔曲线应用-跳动的水滴

    主要通过6个控制点实现. val startPoint = PointF() val endPoint = PointF() val control1 = PointF() val control2 ...

  4. Microsoft Azure Storage Explorer

    上周主管说,要把每次开过的发票,要下载成Pdf的文件,然后就实时的将这些发票存到云上面去. 就是这个Microsoft Azure ,微软的亲儿子. 先把代码贴上来吧,挺简单的. ##.链接账号密码 ...

  5. set statistics profile on实例

    set statistics profile on实例 1.SQL语句实例   SQL 代码   复制 SET STATISTICS PROFILE ON GO SELECT COUNT(b.[Sal ...

  6. 【SQL】联合语句

    一.UNION操作符 UNION 操作符用于合并两个结果集,在合并的同时去掉重复行,并按合并后结果的第一列升序排列.合并后结果集的列名由第一个结果集的列名确定. UINON连接的两个结果集必须具有相同 ...

  7. ORA-03113 ---end-of-file on communication channel 解决方案记录

    ORALCE启动时报如下错误: ORA-03113: end-of-file on communication channel     解决方案如下: 1.查看orcle启动日志,确定具体是什么原因引 ...

  8. dubbo之泛化引用

    使用泛化调用 泛化接口调用方式主要用于客户端没有 API 接口及模型类元的情况,参数及返回值中的所有 POJO 均用 Map 表示,通常用于框架集成,比如:实现一个通用的服务测试框架,可通过 Gene ...

  9. FormCollection获取请求数据

    public ActionResult Add(FormCollection fm) //通过FormCollection 对象获取表单数据 { string message = "&quo ...

  10. C# 判断字符串是否左包含

    //测试字符串 左包含 //string str = "AAABBBCCC"; //char[] ss = str.ToArray(); //0-8 字符数组 //char[] s ...