蓝书2.4 AC自动机
T1 玄武密码 bzoj 4327
题目大意:
一些字符串 求这些字符串的前缀在母串上的最大匹配长度是多少
思路:
对于所有串建立AC自动机
拿母串在自动机上匹配 对所有点打标记 以及对他们的fail打标记
查询每个串标记最长到哪即可
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
#define inf 2139062143
#define ll long long
#define MAXN 10100100
using namespace std;
inline int read()
{
int x=,f=;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-;ch=getchar();}
while(isdigit(ch)) {x=x*+ch-'';ch=getchar();}
return x*f;
}
struct node{int fail,ch[];}tr[MAXN];
int n,m,vis[MAXN],tot;
char s[MAXN],ch[][];
int hsh(char c)
{
if(c=='E') return ;
if(c=='S') return ;
if(c=='W') return ;
return ;
}
void ins(char *c,int len)
{
int pos=;
for(int i=,k;i<len;i++)
{
k=hsh(c[i]);
if(!tr[pos].ch[k]) tr[pos].ch[k]=++tot;
pos=tr[pos].ch[k];
}
}
int q[MAXN],l=,r=,x;
void build()
{
for(int i=;i<;i++) if(tr[].ch[i]) q[++r]=tr[].ch[i];
while(l<=r)
{
x=q[l++];
for(int i=;i<;i++)
if(tr[x].ch[i]) tr[tr[x].ch[i]].fail=tr[tr[x].fail].ch[i],q[++r]=tr[x].ch[i];
else tr[x].ch[i]=tr[tr[x].fail].ch[i];
}
}
void calc()
{
int pos=,k,x;
for(int i=;i<n;i++)
{
k=hsh(s[i]);
pos=tr[pos].ch[k],vis[pos]=,x=pos;
while(tr[x].fail) vis[x=tr[x].fail]=;
}
}
int query(char *c,int len)
{
int pos=,k,res=;
for(int i=;i<len;i++)
{
k=hsh(c[i]);
pos=tr[pos].ch[k];
if(vis[pos]) res=max(res,i+);
else break;
}
return res;
}
int main()
{
n=read(),m=read();
scanf("%s",s);
for(int i=;i<=m;i++)
{scanf("%s",ch[i]);ins(ch[i],strlen(ch[i]));}
build();calc();
for(int i=;i<=m;i++) printf("%d\n",query(ch[i],strlen(ch[i])));
}
T2 Censoring bzoj 3940
题目大意:
有个串S和一些单词 这些单词都不是其他的子串
每次在S中找到最早出现的列表中的单词,然后从S中删除这个单词
重复这个操作直到S中没有列表里的单词为止 输出最后的S
思路:
对于单词建立自动机
因为单词之间没有包含关系 所以可以暴力匹配
在匹配串S时 用手打栈模拟 如果匹配到end就减去这个单词 pos变为之前的pos
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
#define inf 2139062143
#define ll long long
#define MAXN 100100
using namespace std;
inline int read()
{
int x=,f=;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-;ch=getchar();}
while(isdigit(ch)) {x=x*+ch-'';ch=getchar();}
return x*f;
}
struct node{int fail,ch[];}tr[MAXN];
int n,m,vis[MAXN],tot,ed[MAXN],top,p[MAXN];
char s[MAXN],ch[MAXN],st[MAXN];
void ins(char *c,int len)
{
int pos=;
for(int i=,k;i<len;i++)
{
k=c[i]-'a';
if(!tr[pos].ch[k]) tr[pos].ch[k]=++tot;
pos=tr[pos].ch[k];
}
ed[pos]=len;
}
int q[MAXN],l=,r=,x;
void build()
{
for(int i=;i<;i++) if(tr[].ch[i]) q[++r]=tr[].ch[i];
while(l<=r)
{
x=q[l++];
for(int i=;i<;i++)
if(tr[x].ch[i]) tr[tr[x].ch[i]].fail=tr[tr[x].fail].ch[i],q[++r]=tr[x].ch[i];
else tr[x].ch[i]=tr[tr[x].fail].ch[i];
}
}
int main()
{
scanf("%s",s);n=strlen(s),m=read();
while(m--) {scanf("%s",ch);ins(ch,strlen(ch));}
build();
for(int i=,pos=;i<n;i++)
{
st[++top]=s[i];
pos=tr[pos].ch[s[i]-'a'],p[top]=pos;
if(ed[pos]) top-=ed[pos],pos=p[top];
}
for(int i=;i<=top;i++) printf("%c",st[i]);
}
T3 单词 bzoj 3172
题目大意:
一篇论文是由许多单词组成 求每个单词分别在论文中出现多少次 (论文为所有单词加#拼接起来)
思路:
建立ac自动机 可以想到如果一个单词出现那么它的fail也一定出现一次
每次加入一个单词对经过的节点加一 把每个节点的值加入它所有fail的值
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
#define inf 2139062143
#define ll long long
#define MAXN 1001000
using namespace std;
inline int read()
{
int x=,f=;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-;ch=getchar();}
while(isdigit(ch)) {x=x*+ch-'';ch=getchar();}
return x*f;
}
struct node{int fail,ch[];}tr[MAXN];
int n,m,tot,ed[MAXN],g[MAXN],val[MAXN];
char ch[MAXN];
void ins(char *c,int len,int x)
{
int pos=;
for(int i=,k;i<len;i++)
{
k=c[i]-'a';
if(!tr[pos].ch[k]) tr[pos].ch[k]=++tot;
pos=tr[pos].ch[k],val[pos]++;
}
g[x]=pos;
}
int q[MAXN],l=,r=,x;
void build()
{
for(int i=;i<;i++) if(tr[].ch[i]) q[++r]=tr[].ch[i];
while(l<=r)
{
x=q[l++];
for(int i=;i<;i++)
if(tr[x].ch[i]) tr[tr[x].ch[i]].fail=tr[tr[x].fail].ch[i],q[++r]=tr[x].ch[i];
else tr[x].ch[i]=tr[tr[x].fail].ch[i];
}
}
int main()
{
m=read();
for(int i=;i<=m;i++) {scanf("%s",ch);ins(ch,strlen(ch),i);}
build();
for(int i=tot;i>=;i--) val[tr[q[i]].fail]+=val[q[i]];
for(int i=;i<=m;i++) printf("%d\n",val[g[i]]);
}
T4 最短母串 bzoj 1195
题目大意:
n个字符串,要求找到一个最短的字符串T,使得这n个字符串都是T的子串
思路:
① 使用AC自动机 对每个end像状压一样标记 传递到fail上
从a到z bfs 如果bfs到状态访问过所有串 就结束
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
#define inf 2139062143
#define ll long long
#define MAXN 610*(1<<12)
using namespace std;
inline int read()
{
int x=,f=;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-;ch=getchar();}
while(isdigit(ch)) {x=x*+ch-'';ch=getchar();}
return x*f;
}
struct node{int fail,ch[];}tr[];
int n,m,tot,ed[],t;
char ch[];
void ins(char *c,int len,int x)
{
int pos=;
for(int i=,k;i<len;i++)
{
k=c[i]-'A';
if(!tr[pos].ch[k]) tr[pos].ch[k]=++tot;
pos=tr[pos].ch[k];
}
ed[pos]|=(<<x);
}
int q[],l=,r=,x;
void build()
{
for(int i=;i<;i++) if(tr[].ch[i]) q[++r]=tr[].ch[i];
while(l<=r)
{
x=q[l++];
for(int i=;i<;i++)
if(tr[x].ch[i])
tr[tr[x].ch[i]].fail=tr[tr[x].fail].ch[i],ed[tr[x].ch[i]]|=ed[tr[tr[x].fail].ch[i]],q[++r]=tr[x].ch[i];
else tr[x].ch[i]=tr[tr[x].fail].ch[i];
}
}
struct data {int pos,val;}g[MAXN];
queue <data> que;
int ans[],res,hd,tl,vis[][<<];
void bfs()
{
t=(<<n)-,hd=tl=;que.push((data){,});
while(hd<=tl)
{
int p=que.front().pos,st=que.front().val;que.pop();
if(st==t)
{
while(hd>) ans[++res]=g[hd].val,hd=g[hd].pos;
for(int i=res;i;i--) printf("%c",ans[i]+'A');
return ;
}
for(int i=;i<;i++)
if(!vis[tr[p].ch[i]][st|ed[tr[p].ch[i]]])
{
g[++tl]=(data){hd,i};
que.push((data){tr[p].ch[i],(st|ed[tr[p].ch[i]])});
vis[tr[p].ch[i]][st|ed[tr[p].ch[i]]]=;
}
hd++;
}
}
int main()
{
n=read();
for(int i=;i<n;i++) {scanf("%s",ch);ins(ch,strlen(ch),i);}
build();bfs();
}
② 状压 dp i j 表示 已经加入字符的状态为i j结尾的最小长度 同时开一个数组记录这个串 方便字符串比较
转移的时候枚举一下不在状态里的字符即可
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
#define inf 2139062143
#define ll long long
using namespace std;
inline int read()
{
int x=,f=;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-;ch=getchar();}
while(isdigit(ch)) {x=x*+ch-'';ch=getchar();}
return x*f;
}
int n,k,dp[<<][],add[][],ban[],t,res=inf,kd;
char ch[][],ans[<<][][],tmp[];
int calc(char *y,char *x)
{
int l=strlen(y),t=min(strlen(x),strlen(y));
for(int j,i=t;i;i--)
{
for(j=;j<i;j++)
if(x[j]!=y[l-i+j]) break;
if(j==i) return i;
}
return ;
}
void mdf(char *x,char *y,int k)
{
int lx=strlen(x),ly=strlen(y);
for(int i=;i<lx;i++) tmp[i]=x[i];
for(int i=k;i<ly;i++) tmp[lx+i-k]=y[i];
}
int cmp(char *x,char *y)
{
int l=strlen(x);
for(int i=;i<l;i++)
if(x[i]<y[i]) return ;
else if(x[i]>y[i]) return ;
return ;
}
int main()
{
n=read();memset(dp,,sizeof(dp));
for(int i=;i<n;i++) scanf("%s",ch[i]);
for(int i=;i<n;i++)
for(int j=;j<n;j++)
{
if(i==j) continue;
memset(tmp,,sizeof(tmp));
for(int k=,l=;k<strlen(ch[i]);k++)
{
tmp[l++]=ch[i][k];
if(calc(tmp,ch[j])==strlen(ch[j])) ban[j]=;
}
}
for(int i=;i<n;i++) if(!ban[i])
{
if(i==k) {k++;continue;}
memset(ch[k],,sizeof(ch[k]));
for(int j=,l=strlen(ch[i]);j<l;j++) ch[k][j]=ch[i][j];
k++;
}
n=max(,k),t=(<<n)-;
for(int i=;i<n;i++)
for(int j=;j<n;j++)
if(i!=j) add[i][j]=calc(ch[i],ch[j]);
for(int i=;i<n;i++)
{
dp[<<i][i]=strlen(ch[i]);
for(int j=,l=strlen(ch[i]);j<l;j++) ans[<<i][i][j]=ch[i][j];
}
for(int i=;i<t;i++)
for(int j=;j<n;j++)
if(((<<j)&i)==)
{
for(int k=;k<n;k++)
if((<<k)&i)
if(dp[i|(<<j)][j]==dp[i][k]+strlen(ch[j])-add[k][j])
{
mdf(ans[i][k],ch[j],add[k][j]);
if(!cmp(ans[i|(<<j)][j],tmp))
{
memset(ans[i|(<<j)][j],,sizeof(ans[i|(<<j)][j]));
for(int l=;l<dp[i|(<<j)][j];l++)
ans[i|(<<j)][j][l]=tmp[l];
}
}
else if(dp[i|(<<j)][j]>dp[i][k]+strlen(ch[j])-add[k][j])
{
dp[i|(<<j)][j]=dp[i][k]+strlen(ch[j])-add[k][j];
mdf(ans[i][k],ch[j],add[k][j]);
memset(ans[i|(<<j)][j],,sizeof(ans[i|(<<j)][j]));
for(int l=;l<dp[i|(<<j)][j];l++)
ans[i|(<<j)][j][l]=tmp[l];
}
if(i|(<<j)==t&&res==dp[t][j]&&cmp(ans[t][j],ans[t][kd])) kd=j;
if(i|(<<j)==t&&res>dp[t][j]) res=dp[t][j],kd=j;
}
printf("%s",ans[t][kd]);
}
T5 病毒 bzoj 2938
题目大意:
询问是否有一个无限长的01串满足任意一个给出的串都不是它的自串
思路:
把end的标记传递 dfs找环 如果有环就说明可以找到一个满足题意的串
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
#define inf 2139062143
#define ll long long
#define MAXN 30100
using namespace std;
inline int read()
{
int x=,f=;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-;ch=getchar();}
while(isdigit(ch)) {x=x*+ch-'';ch=getchar();}
return x*f;
}
struct node{int fail,ch[];}tr[MAXN];
int n,m,tot,ed[MAXN],t,vis[MAXN],tag[MAXN];
char ch[MAXN];
void ins(char *c,int len)
{
int pos=;
for(int i=,k;i<len;i++)
{
k=c[i]-'';
if(!tr[pos].ch[k]) tr[pos].ch[k]=++tot;
pos=tr[pos].ch[k];
}
ed[pos]=;
}
int q[MAXN],l=,r=,x;
void build()
{
for(int i=;i<;i++) if(tr[].ch[i]) q[++r]=tr[].ch[i];
while(l<=r)
{
x=q[l++];
for(int i=;i<;i++)
if(tr[x].ch[i])
tr[tr[x].ch[i]].fail=tr[tr[x].fail].ch[i],ed[tr[x].ch[i]]|=ed[tr[tr[x].fail].ch[i]],q[++r]=tr[x].ch[i];
else tr[x].ch[i]=tr[tr[x].fail].ch[i];
}
}
int dfs(int x)
{
vis[x]=;
for(int i=;i<;i++)
{
if(vis[tr[x].ch[i]]) return ;
if(tag[tr[x].ch[i]]||ed[tr[x].ch[i]]) continue;
tag[tr[x].ch[i]]=;
if(dfs(tr[x].ch[i])) return ;
}
vis[x]=;return ;
}
int main()
{
n=read();
for(int i=;i<n;i++) {scanf("%s",ch);ins(ch,strlen(ch));}
build();puts(dfs()?"TAK":"NIE");
}
T6 文本生成器 bzoj 1030
蓝书2.4 AC自动机的更多相关文章
- 【POJ2778】DNA Sequence 【AC自动机,dp,矩阵快速幂】
题意 题目给出m(m<=10)个仅仅由A,T,C,G组成的单词(单词长度不超过10),然后给出一个整数n(n<=2000000000),问你用这四个字母组成一个长度为n的长文本,有多少种组 ...
- LA_3942 LA_4670 从字典树到AC自动机
首先看第一题,一道DP+字典树的题目,具体中文题意和题解见训练指南209页. 初看这题模型还很难想,看过蓝书提示之后发现,这实际上是一个标准DP题目:通过数组来储存后缀节点的出现次数.也就是用一颗字典 ...
- HDU 2243 考研路茫茫——单词情结(AC自动机+矩阵)
考研路茫茫——单词情结 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total ...
- LA 4670 (AC自动机 模板题) Dominating Patterns
AC自动机大名叫Aho-Corasick Automata,不知道的还以为是能自动AC的呢,虽然它确实能帮你AC一些题目.=_=|| AC自动机看了好几天了,作用就是多个模式串在文本串上的匹配. 因为 ...
- HDU 5164Matching on Array(AC自动机)
这是BC上的一道题,当时比赛没有做,回头看看题解,说是AC自动机,想着没有写过AC自动机,于是便试着抄抄白书的模板,硬是搞了我数个小时2000ms时限1800过了= = ! 这里就直接贴上BC的结题报 ...
- hdu2243之AC自动机+矩阵乘法
考研路茫茫——单词情结 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Tota ...
- HDU 2243考研路茫茫——单词情结 (AC自动机+矩阵快速幂)
背单词,始终是复习英语的重要环节.在荒废了3年大学生涯后,Lele也终于要开始背单词了. 一天,Lele在某本单词书上看到了一个根据词根来背单词的方法.比如"ab",放在单词前一般 ...
- hdu2243 考研路茫茫——单词情结【AC自动机】【矩阵快速幂】
考研路茫茫——单词情结 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total ...
- AC自动机学习笔记-2(Trie图&&last优化)
我是连月更都做不到的蒟蒻博主QwQ 考虑到我太菜了,考完noip就要退役了,所以我决定还是把博客的倒数第二篇博客给写了,也算是填了一个坑吧.(最后一篇?当然是悲怆のnoip退役记啦QAQ) 所以我们今 ...
随机推荐
- windows系统查看目录树
进入到需要查看的目录后,按住shift键,然后单击鼠标右键,选择“在此处打开命令窗口”,然后命令行窗口界面. 1.若是只查看该目录下有哪些目录,输入tree即可 2.若是显示该目录及其子目录下的所有目 ...
- 分布式集群环境下运行Wordcount程序
1.分布式环境的Hadoop提交作业方式与本地安装的Hadoop作业提交方式相似,但有两点不同: 1)作业输入输出都存储在HDFS 2)本地Hadoop提交作业时将作业放在本地JVM执行,而分布式集群 ...
- PHP读取超大的excel文件数据的方案
场景和痛点 说明 今天因为一个老同学找我,说自己公司的物流业务都是现在用excel处理,按月因为数据量大,一个excel差不多有百万数据,文件有接近100M,打开和搜索就相当的慢 联想到场景:要导入数 ...
- CURL PHP模拟浏览器get和post
模拟浏览器get和post数据需要经常用到的类, 在这里收藏了几个不错的方法 方法一 <?php define ( 'IS_PROXY', true ); //是否启用代理 /* cookie文 ...
- RNN与情感分类问题实战-加载IMDB数据集
目录 Sentiment Analysis Two approaches Single layer Multi-layers Sentiment Analysis Two approaches Sim ...
- list嵌套,int与str的用法,replace
#*************************replace(待改,改动值),返回很重要 A = [['libai',89]] A[0][0]=A[0][0].replace('a','af') ...
- Linux基础命令回顾
前言 说到linux基础命令,网上一搜一箩筐,想学也有很多教程,如果你不幸看到此篇文章,想看就认真看完,毕竟你点进来了不是嘛? 我每次写的文章都是为了分享自己的学习成果或重要知识点,希望能帮助更多的人 ...
- Vue如何引入jquery实现平滑滚动到指定位置效果
在以往的做法里首选jquery的animate实现,但是Vue里并没有这个方法.如何在Vue项目中实现点击导航平滑滚动到指定位置,为了这效果我是快要崩溃了,上网查阅了很久发现并没有真正意义上解决这个问 ...
- h-ui.admin.pro.iframe头部和标签Tab修改CSS
原效果:头部高度偏高,tab标签不太好看 修改后:缩小高度,调整tab标签css样式 百度网盘链接:https://pan.baidu.com/s/1qknPNAMGL7BFUIsleOF9M ...
- Python字典(Dictionary)
Python字典 字典的每个键值(key=>value)对用冒号(:)分割,每个对之间用逗号(,)分割,整个字典包括在花括号({})中 ,格式如下所示: d = {key1 : value1, ...