蓝书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) 所以我们今 ...
随机推荐
- MYSQL每日一学 - 时间间隔表达式
参考链接:https://dev.mysql.com/doc/refman/5.7/en/expressions.html Interval表达式(Temporal intervals)的使用 Int ...
- Linux中CentOS网络配置以及与Xshell建立远程连接
为centos配置网络 (1)第一步 点开虚拟机的设置,如下图做相关的设置: 网络连接要选择桥接模式,其他的勾选就按照上图的即可,勾选完成点击确定. (2)第二步 点击VMware的编辑选项,找到“虚 ...
- yum 软件管理器
yum软件管理器 yum是一个强大的软件包管理器,能够自动解决安装时rpm包之间的依赖关系. 一.使用yum管理软件包 1.使用命令 yum help 查看使用方法 [root@majinhai ~] ...
- Python:socket实现ftp程序
刚开始学习socket编程,还不是特熟练,码了好长时间,中间遇到许多问题,记录一下用socketserver写ftp server端: #!/usr/bin/env python import soc ...
- ResNet实战
目录 Res Block ResNet18 Out of memory # Resnet.py #!/usr/bin/env python # -*- coding:utf-8 -*- import ...
- python while、continue、break
while循环实现用户登录 _user = "tom" _passwd = "abc123" counter = 0 while counter < 3: ...
- LeetCode(38) Count and Say
题目 The count-and-say sequence is the sequence of integers beginning as follows: 1, 11, 21, 1211, 111 ...
- IIS 注册.NET Framework 4.0 命令
cmd执行以下命令 32位Windows:C:\Windows\Microsoft.NET\Framework\v4.0.30319\aspnet_regiis.exe -i 64位Windows:C ...
- 7-19 求链式线性表的倒数第K项(20 分)(单链表定义与尾插法)
给定一系列正整数,请设计一个尽可能高效的算法,查找倒数第K个位置上的数字. 输入格式: 输入首先给出一个正整数K,随后是若干正整数,最后以一个负整数表示结尾(该负数不算在序列内,不要处理). 输出格式 ...
- Django开发:(1)django基础 & url控制器
HTTP请求协议 HTTP协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于万维网(WWW:World Wide Web )服务器与本地浏览器之间传输超文本 ...