AC自动机 P3808 P3796

第一次写AC自动机

简单版的这道题可以在进行匹配的时候剪一下枝,应为之前比配过了,不用在匹配了。

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <queue>
using namespace std;
const int MAXN=1e6+10;
queue<int> que;

struct AC{
    int tr[MAXN][26],fail[MAXN],val[MAXN],tot;

    void insert(char *s){
        int n=strlen(s+1),p=0;
        for(int i=1;i<=n;i++){
            if(!tr[p][s[i]-'a'])
                tr[p][s[i]-'a']=++tot;
            p=tr[p][s[i]-'a'];
        }
        val[p]++;
    }

    void getfail(){
        while(!que.empty()) que.pop();
        for(int i=0;i<26;i++)
            if(tr[0][i])
                que.push(tr[0][i]);
        while(!que.empty()){
            int u=que.front();que.pop();
            for(int i=0;i<26;i++)
                if(tr[u][i]) fail[tr[u][i]]=tr[fail[u]][i],que.push(tr[u][i]);
                else tr[u][i]=tr[fail[u]][i];
        }
        // for(int i=0;i<=tot;i++) cout<<fail[i]<<" ";cout<<endl;
        // for(int i=0;i<=tot;i++) cout<<val[i]<<" ";cout<<endl;
    }

    int ask(char *s){
        int n=strlen(s+1),p=0,ans=0;
        for(int i=1;i<=n;i++){
            p=tr[p][s[i]-'a'];
            for(int t=p;t&&val[t]!=-1;t=fail[t])
                ans+=val[t],val[t]=-1;
        }
        return ans;
    }
}ac;

int n;
char s[MAXN];

int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%s",s+1);
        ac.insert(s);
    }
    ac.getfail();
    scanf("%s",s+1);
    int ans=ac.ask(s);
    printf("%d\n",ans);
    return 0;
}

加强版的题,就不能剪枝了,因为要求出每个模式串出现的次数。

可以在 #trie# 的末节点上记录该串的 #id# 然后每次匹配到就给它++就行了。

#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <queue>
using namespace std;
const int MAXN=1e6+10;
queue<int> que;
int cnt[160],ans,n;
char s[160][100],str[MAXN];

struct AC{
    int tr[16000][26],fail[MAXN],val[MAXN],tot;

    void init(){
        memset(cnt,0,sizeof(cnt));
        memset(tr,0,sizeof(tr));
        memset(fail,0,sizeof(fail));
        memset(val,0,sizeof(val));
        tot=0;
        ans=0;
    }

    void insert(char *s,int id){
        int n=strlen(s+1),p=0;
        for(int i=1;i<=n;i++){
            if(!tr[p][s[i]-'a']) tr[p][s[i]-'a']=++tot;
            p=tr[p][s[i]-'a'];
        }
        val[p]=id;
    }

    void getfail(){
        while(!que.empty()) que.pop();
        for(int i=0;i<26;i++)
            if(tr[0][i]) que.push(tr[0][i]);
        while(!que.empty()){
            int u=que.front();que.pop();
            for(int i=0;i<26;i++)
                if(tr[u][i]) fail[tr[u][i]]=tr[fail[u]][i],que.push(tr[u][i]);
                else tr[u][i]=tr[fail[u]][i];
        }
    }

    void ask(char *s){
        int n=strlen(s+1),p=0;
        for(int i=1;i<=n;i++){
            p=tr[p][s[i]-'a'];
            for(int t=p;t;t=fail[t])
                if(val[t]) cnt[val[t]]++,ans=max(ans,cnt[val[t]]);
        }
    }
}ac;

int main(){
    while(~scanf("%d",&n)){
        if(!n) break;
        ac.init();
        for(int i=1;i<=n;i++)
            scanf("%s",s[i]+1),
            ac.insert(s[i],i);
        ac.getfail();
        scanf("%s",str+1);
        ac.ask(str);
        printf("%d\n",ans);
        for(int i=1;i<=n;i++) if(cnt[i]==ans) printf("%s\n",s[i]+1);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/BakaCirno/p/11771700.html

时间: 2024-08-28 16:44:04

AC自动机 P3808 P3796的相关文章

luogu P3808 【模板】AC自动机(简单版)

二次联通门 : luogu P3808 [模板]AC自动机(简单版) /* luogu P3808 [模板]AC自动机(简单版) 手速越来越快了 10分钟一个AC自动机 一遍过编译 + 一边AC 感觉不错 我也就做做板子题了.. */ #include <iostream> #include <cstring> #include <cstdio> #include <queue> #define Max 1000009 void read (int &

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

[题解]P3796 [模板]AC自动机(加强版) 记录当前\(cnt\)是第几个"星".记录第几个串是对应着第几个星. 这里补充一点对于\(AC\)自动机的理解.可能一直有个问题我没有想明白,就是打标记的点只有一个,然而匹配时,假若一个分支包括了另一个不同的分支该怎么办.实际上,我们可以在匹配的时候使用\(fail\)数组进行类似链式前向星的遍历,从而遍历到那个打标记的地方.那么问题来了,怎么保证链式前向星会遍历到那个打了标记的节点呢?答案就在\(gen\_fail\)的玄机里.\(g

P3808 【模版】AC自动机(简单版)

题目背景 这是一道简单的AC自动机模版题. 用于检测正确性以及算法常数. 为了防止卡OJ,在保证正确的基础上只有两组数据,请不要恶意提交. 题目描述 给定n个模式串和1个文本串,求有多少个模式串在文本串里出现过. 输入输出格式 输入格式: 第一行一个n,表示模式串个数: 下面n行每行一个模式串: 下面一行一个文本串. 输出格式: 一个数表示答案 输入输出样例 输入样例#1: 2 a aa aa 输出样例#1: 2 说明 subtask1[50pts]:∑length(模式串)<=10^6,len

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

题目描述 有个由小写字母组成的模式串以及一个文本串.每个模式串可能会在文本串中出现多次.你需要找出哪些模式串在文本串中出现的次数最多. 输入输出格式 输入格式: 输入含多组数据. 每组数据的第一行为一个正整数,表示共有个模式串,. 接下去行,每行一个长度小于等于的模式串.下一行是一个长度小于等于的文本串. 输入结束标志为. 输出格式: 对于每组数据,第一行输出模式串最多出现的次数,接下去若干行每行输出一个出现次数最多的模式串,按输入顺序排列. 输入输出样例 输入样例#1: 2 aba bab a

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

嘟嘟嘟 这个和某谷的AC自动机模板简单版差不多. 但还是要注意几点的: 1.这个是统计出现次数,而不是是否出现,所以在查询的时候加上这个节点的val后,不能把val标记为-1.那么也就可以说查询的时间复杂度能比简单版的稍微第一慢一点. 2.考虑k个一样的模式串:刚开始我想的是每一个节点开一个vector,记录这里是第几个模式串.但其实没有这个必要,对于相同的模式串,我们只用记录任意一个就行,反而在出现次数上要都加上.因为如果主串中存在这些相同的模式串,那么出现次数应该是出现次数 * k.输出的时

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

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

P3808 【模板】AC自动机(简单版)

题意 AC自动机模版题. 传送门 Code #include <bits/stdc++.h> using namespace std; const int maxn = 1e6+10; int fail[maxn], e[maxn], tree[maxn][26], tot; void insert(char *t) { int p = 0; for (int x, i=0; t[i]; ++i) { x = t[i]-'a'; if(!tree[p][x]) tree[p][x] = ++t

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=

AC自动机详解

首先,看清楚了,这是AC自动机不是自动AC机 引用AC自动机模板题上的一句话: ovo 在学习AC自动机之前,应该先掌握一下两个前置技能: Trie KMP AC自动机,通俗地讲,就是在Trie上跑KMP.AC自动机利用Trie的性质和KMP的思想,可以实现字符串的多模匹配.KMP是单模匹配,而它与AC自动机最大的区别就在fail指针的求法,其余思想基本相同. 所谓多模匹配,即给出若干个模式串和一个文本串,需要查找这些模式串在文本串中出现的情况. AC自动机的操作分为三步: 建树 求fail指针