前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >P3796 【模板】AC自动机(加强版)

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

作者头像
attack
发布2018-04-12 14:42:20
5270
发布2018-04-12 14:42:20
举报

题目描述

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

输入输出格式

输入格式:输入含多组数据。

每组数据的第一行为一个正整数N,表示共有N个模式串,1<=N<=150

接下去N行,每行一个长度小于等于70的模式串。下一行是一个长度小于等于10^6 的文本串T。

输入结束标志为N=0。

输出格式:

对于每组数据,第一行输出模式串最多出现的次数,接下去若干行每行输出一个出现次数最多的模式串,按输入顺序排列。

输入输出样例

输入样例#1:

2
aba
bab
ababababac
6
beta
alpha
haha
delta
dede
tata
dedeltalphahahahototatalpha
0

输出样例#1:

4
aba
2
alpha
haha

就是个纯模板,没啥好说的,。

不过AC自动机真的是,,恶心到爆,,,

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<cmath>
  5 #include<queue>
  6 #include<algorithm>
  7 #include<map>
  8 using namespace std;
  9 const int MAXN=100001;
 10 void read(int &n)
 11 {
 12     char c='+';int x=0;bool flag=0;
 13     while(c<'0'||c>'9'){c=getchar();if(c=='-')flag=1;}
 14     while(c>='0'&&c<='9'){x=x*10+c-48;c=getchar();}
 15     flag==1?n=-x:n=x;
 16 }
 17 char s[MAXN][101];
 18 char text[1000001];
 19 struct AC_Automata
 20 {
 21     int sz;
 22     int ch[MAXN][28];//trie树 
 23     int cnt[MAXN];// 每个单词出现的次数
 24     int val[MAXN];// 记录是否有单词
 25     int last[MAXN];
 26     int f[MAXN]; 
 27     int sigma_sz;
 28     map<string,int>mp;//出现的位置
 29     void Init()
 30     {
 31         memset(ch[0],0,sizeof(ch[0]));
 32         memset(cnt,0,sizeof(cnt));
 33         mp.clear();
 34         sz=1;    
 35         sigma_sz=26;
 36     }
 37     int idx(char c)
 38     {
 39         return c-'a';
 40     }
 41     void Insert(char *s,int pos)
 42     {
 43         int l=strlen(s); int now=0;
 44         for(int i=0;i<l;i++)
 45         {
 46             int c=idx(s[i]);
 47             if(!ch[now][c])
 48             {
 49                 memset(ch[sz],0,sizeof(ch[sz]));
 50                 val[sz]=0;
 51                 ch[now][c]=sz++;
 52             }
 53             now=ch[now][c];
 54         }
 55         val[now]=pos;//一个单词的结束
 56         mp[string(s)]=pos; // 记录这个字符串出现的位置,按顺序输出 
 57     }
 58     void getfail()
 59     {
 60         f[0]=0;
 61         queue<int>q;
 62         for(int i=0;i<sigma_sz;i++)
 63         {
 64             int u=ch[0][i];
 65             if(u)// 此处有单词出现
 66             {    
 67                 f[u]=0;// 连向根节点
 68                 q.push(u);
 69                 last[u]=0;// 上一个出现的单词一定是根节点    
 70                 // 上面两条语句没什么卵用,只是为了帮助理解AC自动机的含义 
 71             }
 72         }
 73         while(!q.empty())
 74         {
 75             int p=q.front();q.pop();
 76             for(int i=0;i<sigma_sz;i++)
 77             {
 78                 int u=ch[p][i];
 79                 if(!u)//没有孩子
 80                 {
 81                     ch[p][i]=ch[f[p]][i];// 补上一条不存在的边,减少查找次数 
 82                     continue;    
 83                 }
 84                 q.push(u);
 85                 int v=f[p];// 找到它的失陪指针
 86                 f[u]=ch[v][i];//因为我们在上面补上了一条不存在边,所以他一定存在孩子 
 87                 last[u]=val[f[u]] ? f[u] : last[f[u]];
 88                 //判断下是不是单词结尾  是, 不是
 89             }     
 90         }
 91     }
 92     int ok(int pos)
 93     {
 94         if(pos)
 95         {
 96             cnt[val[pos]]++;
 97             ok(last[pos]);
 98         }
 99     }
100     void find(char *s)// 查找模式串 
101     {
102         int l=strlen(s);
103         int j=0;// 当前节点的编号
104         for(int i=0;i<l;i++)
105         {
106             int c=idx(s[i]);
107             while(j&&!ch[j][c])    
108             j=f[j];// 顺着失配边走
109             j=ch[j][c]; // 取到儿子
110             if(val[j])    
111             ok(j);// 找到一个单词
112             else if(last[j])    
113             ok(last[j]);// 当前节点不行就看看上一个单词出现的位置行不行 
114         } 
115     }
116 }ac;
117 int n;
118 int main()
119 {
120     while(scanf("%d",&n)==1&&n!=0)
121     {
122         ac.Init();
123         for(int i=1;i<=n;i++)
124         {
125             scanf("%s",s[i]);
126             ac.Insert(s[i],i);
127         }
128         ac.getfail();
129         scanf("%s",text);
130         ac.find(text);
131         int ans=-1;
132         for(int i=1;i<=n;i++)
133             if(ac.cnt[i]>ans) ans=ac.cnt[i];
134         printf("%d\n",ans);
135         for(int i=1;i<=n;i++)
136             if(ac.cnt[i]==ans)
137                 printf("%s\n",s[i]);
138     }
139     return 0;
140 }
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017-07-06 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 题目描述
  • 输入输出格式
  • 输入输出样例
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档