蓝书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) 所以我们今 ...
随机推荐
- 【spring】jar包详解与模块依赖关系
以spring3.X为例 jar包详解 1. spring-core.jar:包含Spring框架基本的核心工具类,Spring其它组件要都要使用到这个包里的类,是其它组件的基本核心: 2. spri ...
- 转:Centos7安装zabbix3.4超详细步骤解析
安装前准备: 1.1 安装依赖包: yum -y install wget net-snmp-devel OpenIPMI-devel httpd openssl-devel java lrzsz f ...
- scrapy实现全站抓取数据
1. scrapy.CrawlSpider scrapy框架提供了多种类型的spider,大致分为两类,一类为基本spider(scrapy.Spider),另一类为通用spider(scrapy.s ...
- (十三)python 3 集合
定义: 1.不同元素组成 2.无序 3.集合中的元素必须是不可变类型 创建集合 s = {1,2,3,4,5,6,7,8} 1.定义可变集合 >>> set_test = set(' ...
- Open DBDiff 0.9
SQL Server 迁移过程经常会的出现,需要比对两个数据库之间,或者是表之间到底有何不同 SQL server 自带的tablediff Utility 是一个命令行的工具,对于偶尔需要做一次的体 ...
- vs2003 刷新项目失败。无法从服务器中检索文件夹信息
环境: 操作系统:windows server 2003 开发工具:Visual stuadio 2003 FrameWork: 1.1 打开web项目的时候报错 提示 项目刷新失败,无法从服务器 ...
- java项目连接access数据库
1.导入Access_JDBC30.jar到项目中 jar包百度云链接:https://pan.baidu.com/s/10HFM3HomMArvfHjklA_1MA 密码:0qxp 项目名称-> ...
- Python xml文件处理
什么是XML文件? xml即可扩展标记语言,它可以用来标记数据.定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言. 从结构上,很像HTML超文本标记语言.但他们被设计的目的是不同的,具体如 ...
- HDU 3157 Crazy Circuits
Crazy Circuits Time Limit: 2000ms Memory Limit: 32768KB This problem will be judged on HDU. Original ...
- POJ 2104 K-th Number (可持久化线段树)
题目大意 给一个长度为n的序列,有m个询问,每次询问一个区间里面第k小的数. 解题分析 静态的区间第k大.复习了一下可持久化线段树. 首先对数值离散化,建一颗权值线段树.按照序列的顺序依次插入,每一个 ...