【题解】P3796【模板】AC自动机(加强版)

【题解】P3796 【模板】AC自动机(加强版)

记录当前\(cnt\)是第几个"星"。记录第几个串是对应着第几个星。

这里补充一点对于\(AC\)自动机的理解。可能一直有个问题我没有想明白,就是打标记的点只有一个,然而匹配时,假若一个分支包括了另一个不同的分支该怎么办。实际上,我们可以在匹配的时候使用\(fail\)数组进行类似链式前向星的遍历,从而遍历到那个打标记的地方。那么问题来了,怎么保证链式前向星会遍历到那个打了标记的节点呢?答案就在\(gen\_fail\)的玄机里。\(gen\_fail\)时,由于是找和已经匹配部分相同的后缀,所以指向的节点它到\(0\)的长度会越来越小。显然那个有标记点的串是相较其它包括它自己的串中最短的,那么一定会被这个链式前向星遍历到。

上代码

#include<bits/stdc++.h>

using namespace std;
#define RP(t,a,b) for(register int t=(a),edd=(b);t<=edd;++t)
#define DRP(t,a,b) for(register int t=(a),edd=(b);t>=edd;--t)
#define ERP(t,a) for(register int t=head[a];t;t=e[t].nx)
#define Max(a,b) ((a)<(b)?(b):(a))
#define Min(a,b) ((a)<(b)?(a):(b))
#define midd register int mid=(l+r)>>1
#define TMP template < class ccf >

TMP inline ccf qr(ccf b){
    char c=getchar();
    int q=1;
    ccf x=0;
    while(c<48||c>57)
    q=c==45?-1:q,c=getchar();
    while(c>=48&&c<=57)
    x=x*10+c-48,c=getchar();
    return q==-1?-x:x;
}
const int maxn=155;
struct AC{
    int fail;
    int son[27];
    int cnt;
    inline int& operator [](int x){
    return son[x];
    }
    inline int& operator [](char x){
    return son[x-‘a‘+1];
    }
}ac[maxn*70];

string x[maxn];
int toac[maxn];
int acnt[maxn];
int macnt[maxn];
int act;
int prcnt;
int n;

inline int build(string p,int len){
    register int now=0;
    RP(t,0,len-1){
    if(!ac[now][p[t]])
        ac[now][p[t]]=++act;
    now=ac[now][p[t]];
    }
    if(!ac[now].cnt)
    ac[now].cnt=++prcnt;
    return ac[now].cnt;
}

queue < int > q;
inline void gen(){
    RP(t,1,26){
    if(ac[0][t])
        q.push(ac[0][t]);
    }
    while(!q.empty()){
    register int now=q.front();
    q.pop();
    RP(t,1,26){
        if(ac[now][t])
        ac[ac[now][t]].fail=ac[ac[now].fail][t],q.push(ac[now][t]);
        else
        ac[now][t]=ac[ac[now].fail][t];
    }
    }
}

inline void match(string p,int len){
    register int now=0;
    RP(t,0,len-1){
    now=ac[now][p[t]];
    for(register int i=now;i;i=ac[i].fail){
        if(ac[i].cnt)
        macnt[ac[i].cnt]++;
    }
    }
}

int main(){
#ifndef ONLINE_JUDGE
    freopen("in.in","r",stdin);
    freopen("out.out","w",stdout);
#endif
    while(  (n=qr(1))  ){
    memset(ac,0,sizeof ac);
    memset(macnt,0,sizeof macnt);
    act=prcnt=0;
    RP(t,1,n){
        cin>>x[t];
        toac[t]=build(x[t],x[t].length());
    }
    gen();
    cin>>x[0];
    match(x[0],x[0].length());
    register int ans=0;
    RP(t,1,prcnt)
        ans=Max(ans,macnt[t]);
    cout<<ans<<endl;
    RP(t,1,n)
        if(macnt[toac[t]]==ans)
        cout<<x[t]<<endl;
    }
    return 0;
}

原文地址:https://www.cnblogs.com/winlere/p/10356911.html

时间: 2024-11-08 07:47:10

【题解】P3796【模板】AC自动机(加强版)的相关文章

算法模板——AC自动机

实现功能——输入N,M,提供一个共计N个单词的词典,然后在最后输入的M个字符串中进行多串匹配(关于AC自动机算法,此处不再赘述,详见:Aho-Corasick 多模式匹配算法.AC自动机详解.考虑到有时候字典会相当稀疏,所以引入了chi和bro指针进行优化——其原理比较类似于邻接表,这个东西本身和next数组本质上是一致的,只是chi和bro用于遍历某一节点下的子节点,next用于查询某节点下是否有需要的子节点) 1 type 2 point=^node; 3 node=record 4 ex:

[模板]AC自动机(1)

题目描述 给定一个文本串和多个模式串,求有几个模式串出现在文本串中 #include <cstdio> #include <cstring> #include <algorithm> #define MAXN 1000005 char s[MAXN]; int N; struct queue{ int que[MAXN];int head,tail; queue():head(1),tail(0){} inline void pop(){head++;} inline

luogu_3966【题解】单词 AC自动机

题面:https://www.luogu.org/problemnew/show/P3966 大意:小张最近在忙毕设,所以一直在读论文.一篇论文是由许多单词组成但小张发现一个单词会在论文中出现很多次,他想知道每个单词分别在论文中出现了多少次. 这次没有文本串. 用了个奇妙的方法. a[x] 表示第x个串结尾的位置. 在bfs搜索求fail的时候手写,将所有的点都保存下来了. num(x)则为经过这个点x的个数. 自然num(fail(x))+=num(x). 所有经过x的串都能经过fail(x)

模板——AC自动机

#include<bits/stdc++.h> using namespace std; struct nob{ int fail,son[27],ed; }a[1000000]; int cnt=0; void build (string s){ int now=0; for (int i=0; i<s.length(); i++){ if (a[now].son[s[i]-'a']==0) a[now].son[s[i]-'a']=++cnt; now=a[now].son[s[i]

AC自动机例题

P3808 [模板]AC自动机(简单版) [题目描述] 给定n个模式串和1个文本串,求有多少个模式串在文本串里出现过. #include<bits/stdc++.h> using namespace std; typedef long long LL; const int INF=1e9+7; inline LL read(){ register LL x=0,f=1;register char c=getchar(); while(c<48||c>57){if(c=='-')f=

HDU 5164Matching on Array(AC自动机)

这是BC上的一道题,当时比赛没有做,回头看看题解,说是AC自动机,想着没有写过AC自动机,于是便试着抄抄白书的模板,硬是搞了我数个小时2000ms时限1800过了= = ! 这里就直接贴上BC的结题报告(#27):http://bestcoder.hdu.edu.cn/solutions.php 1003 Matching on Array 首先我们考虑m=1的情况.给定两个数组A={a1,a2,…,an}和B={b1,b2,…,bk},问B在A中出现了几次.令ci=ai+1ai,1≤i<n,同

[AC自动机]玄武密码

题目描述 一个长度为\(N\)的母串,有四个元素分别是:N,S,W,N. 有M个长度为100的模式串. 现在要求每个模式串的前缀与母串匹配最长长度. 输入样例 7 3 SNNSSNS NNSS NNN WSEE 输出样例 4 2 0 题解 这是AC自动机的模板题 关键是判断模式串的前缀是否被匹配 因为AC自动机的一个结点代表一个前缀,通过fail指针的遍历可以标记所有匹配的前缀 首先将模式串插入到trie树中,构造自动机 再将母串输入到AC自动机中,把匹配到的前缀即结点都标记一下.这里有一个剪枝

【BZOJ1009】【HNOI2008】GT考试 AC自动机+矩阵乘法

广告: #include <stdio.h> int main() { puts("转载请注明出处[vmurder]谢谢"); puts("网址:blog.csdn.net/vmurder/article/details/44003109"); } 题解: 建立AC自动机的过程可以改为KMP. 反正单串233. 代码: #include <queue> #include <cstdio> #include <cstring&

P3796 【模板】AC自动机(加强版) 题解(Aho-Corasick Automation)

题目链接 AC自动机 解题思路 AC自动机模板题. 刚学AC自动机,写一篇博客增强理解. AC自动机最关键的一点在于,\(fail\)失配指针的构造. \(fail\)指针指向的地方,是匹配出现错误后进行重新匹配的位置,这说明,从根开始到\(fail\)指针指向的地方这一块字符串,正是我们刚刚失配之前配上的那一块字符串(子串),且为最长子串.这一点和KMP算法相同. AC代码 #include<stdio.h> #include<string.h> int ac[100010][2