题目大意:有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. 使用IDEA 搭建一个SpringBoot + Hibernate + Gradle

    ---恢复内容开始--- 打开IDEA创建一个新项目: 第一步: 第二步: 第三步: 最后一步: 如果下载的时候时间太久.可以找到build.gradle文件,添加以下代码.如下图 maven{ ur ...

  2. C# 多线程系列(四)

    Parallel类 Parallel类定义了for.foreach和invoke的静态方法.Parallel类使用多个任务,因此使用多个线程来完成这个作业. Parallel.For Parallel ...

  3. 使用CMD建立指定格式的文件

    一.建立空文件的几种方法1.cd.>a.txtcd.表示改变当前目录为当前目录,即等于没改变:而且此命令不会有输出.>表示把命令输出写入到文件.后面跟着a.txt,就表示写入到a.txt. ...

  4. 启动MyEclipse 出现java.lang.RuntimeException: No application id has been found 解决办法

    咋一看,太熟悉了,就去eclipse\links 目录下,发现指定的MyEclipse的路径不对. 突然想起来了,MyEclipse是换地了. MyEclipse里面内置的eclipse找不到MyEc ...

  5. 【sqli-labs】 less15 POST - Blind- Boolian/time Based - Single quotes (基于bool/时间的单引号POST型盲注)

    错误不回显了 构造永真登陆 登陆成功了 虽然登陆成功了,但是数据库的数据还么有爆出来 构造一下用户名 ' or length(database())=8# 如果数据库名的长度不等于8,登陆会失败 猜测 ...

  6. Android进度条控件ProgressBar使用

    ProgressBar有四种样式,圆形的(大,中,小)和直条形的(水平) 对应的style为 <LinearLayout xmlns:android="http://schemas.a ...

  7. vue路由中的 Meta

    在项目中肯定有这样的需求,那就是在某个页面的时候,顶部展示 现在当前的页面路径,如下图: 这个在vue中其实很好实现. 首先出现这个肯定是相对应不同的页面,也就是说对应不同的路由,我们在定义路由的时候 ...

  8. react-native 横向滚动的商品展示

    在app中会有这种页面 像这样商品是横向的,而且要滚动,思路是利用 ScrollView 横向的滚动 思路: (a): 横向滚动的整体作为一个组件  ShopCenter {/*** 横向滚动 *** ...

  9. 关于图片和auido预加载

    预加载老生常谈: funtion preLoadImages(imageArr){ var self = this; var newimages=[], loadedimages=0 var post ...

  10. eas之获取当前登陆信息

    public void getSystemInfo()    {        // SysContext工具类可获取当前登陆用户的信息,可根据需要进行调用.        // 举两例如下:     ...