【AC自动机】hdu2222 hdu2896 hdu3065 zoj3430 poj2778 hdu2243
来源:互联网 发布:中超风云球员数据 编辑:程序博客网 时间:2024/04/28 21:48
AC自动机用于多个模式串与多个母串的匹配。
第一步:根据模式串建立字典树
int len=strlen(w), r=root; for(int i=0;i<len;++i) { if(tree[r].ch[w[i]])r=tree[r].ch[w[i]]; else r=tree[r].ch[w[i]]=++cnt; } ++tree[r].cnt;//cnt为在该节点结束的模式串的数量
第二步:计算每一个节点的fail指针(与kmp中next数组相似)。首先找到u的父亲的fail指针v。若v对应的儿子不为空,则u的fail指针指向v。否则访问v的fail指针。
void bfs(){ head=tail=0; q[++tail]=0; int p, tmp; while(head<tail) { p=q[++head]; for(int i=0;i<128;++i)//根据模式串的字符大小确定i的范围 { if(tree[p].ch[i]) { tmp=tree[p].ch[i]; if(p)tree[tmp].fail=tree[tree[p].fail].ch[i]; q[++tail]=tmp; } else tree[p].ch[i]=tree[tree[p].fail].ch[i]; } }}
第三步:在字典树中查找
void query(){ int len=strlen(w), ans=0, p=0, tmp; for(int i=0;i<len;++i) { p=tree[p].ch[w[i]-'a']; tmp=p; while(tree[tmp].pos) { ans+=tree[tmp].pos; tree[tmp].pos=0; tmp=tree[tmp].fail; } } printf("%d\n",ans);}
模板题:
hdu2222
题目大意:问在一个母串中有多少个模式串出现
直接上模板
#include <iostream>#include <cstdio>#include <cstring>#define MAXN 500005#define MAXM 1000005using namespace std;int n;char w[MAXM];struct node{ int pos, ch[26], fail; inline void init() { fail=pos=0; memset(ch,0,sizeof ch); }}tree[MAXN];int cnt, root;void add(){ int len=strlen(w), r=root; for(int i=0;i<len;++i) { w[i]-='a'; if(tree[r].ch[w[i]])r=tree[r].ch[w[i]]; else r=tree[r].ch[w[i]]=++cnt; } ++tree[r].pos;}int head, tail, q[MAXN];void bfs(){ head=tail=0; q[++tail]=0; int p, tmp; while(head<tail) { p=q[++head]; for(int i=0;i<26;++i) { if(tree[p].ch[i]) { tmp=tree[p].ch[i]; if(p)tree[tmp].fail=tree[tree[p].fail].ch[i]; q[++tail]=tmp; } else tree[p].ch[i]=tree[tree[p].fail].ch[i]; } }}void query(){ int len=strlen(w), ans=0, p=0, tmp; for(int i=0;i<len;++i) { p=tree[p].ch[w[i]-'a']; tmp=p; while(tree[tmp].pos) { ans+=tree[tmp].pos; tree[tmp].pos=0; tmp=tree[tmp].fail; } } printf("%d\n",ans);}int main(){ int cas; scanf("%d",&cas); while(cas--) { cnt=root=0; scanf("%d",&n); for(int i=0;i<n;++i) { scanf("%s",w); add(); } bfs(); scanf("%s",w); query(); for(int i=0;i<=cnt;++i) tree[i].init(); } return 0;}
hdu2896
一定要注意模式串字符的范围。蒟蒻在此处RE了几次才发现…
提供指针版
不过还是数组版的好调试一些 233
#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>#define MAXN 100005#define MAXM 10005using namespace std;int n, m;bool vis[MAXN];struct node{ int pos; node *ch[128], *fail; inline void init() { fail=0, pos=0; memset(ch,0,sizeof ch); }}tree[MAXN], *cnt, *root;char w[MAXM];void add(int j){ int len=strlen(w), v; node *r=root; for(int i=0;i<len;++i) { v=w[i]; if(r->ch[v])r=r->ch[v]; else r=r->ch[v]=++cnt; } r->pos=j; vis[j]=1;}node *q[MAXN];int head, tail;void bfs(){ head=tail=0; q[++tail]=root; node *p, *son, *tmp; while(head<tail) { p=q[++head]; for(int i=0;i<128;++i) { son=p->ch[i]; if(son) { if(p==root)son->fail=p; else { tmp=p->fail; while(tmp) { if(tmp->ch[i]) { son->fail=tmp->ch[i]; break; } tmp=tmp->fail; } if(!tmp)son->fail=root; } q[++tail]=son; } } }}int num[MAXM], tmp, tot;void query(int j){ int len=strlen(w), v; node *p=root, *temp; for(int i=0;i<len;++i) { v=w[i]; while(!p->ch[v]&&p!=root) p=p->fail; p=p->ch[v]; if(!p)p=root; temp=p; while(vis[temp->pos]) { num[++tmp]=temp->pos; vis[temp->pos]=0; temp=temp->fail; } } if(tmp) { sort(num+1,num+tmp+1); printf("web %d:",j); for(int i=1;i<=tmp;++i) { printf(" %d",num[i]); vis[num[i]]=1; } ++tot, tmp=0; puts(""); }}int main(){ while(~scanf("%d",&n)) { root=cnt=tree, tot=0; for(int i=1;i<=n;++i) { scanf("%s",w); add(i); } bfs(); scanf("%d",&m); for(int i=1;i<=m;++i) { scanf("%s",w); query(i); } printf("total: %d\n",tot); for(node *p=tree;p<=cnt;++p) p->init(); } return 0;}
hdu3065
这里就涉及到了计数的问题。
不进行标记。在之前的模板中都加了一个优化,即访问了一个节点就打上标记。
#include <iostream>#include <cstdio>#include <cstring>#define MAXM 2000005#define MAXN 50005using namespace std;int n, ans[1005];char w[1005][55], s[MAXM];struct node{ node *ch[26], *fail; int pos; void init() { fail=0, pos=0; memset(ch,0,sizeof ch); }}tree[MAXN], *root, *cnt;void add(node *r,int j){ int len=strlen(w[j]), v; for(int i=0;i<len;++i) { v=w[j][i]-'A'; if(r->ch[v])r=r->ch[v]; else r=r->ch[v]=++cnt; } r->pos=j;}int head, tail;node *q[MAXN];void bfs(){ head=tail=0; q[++tail]=root; node *p, *son, *tmp; while(head<tail) { p=q[++head]; for(int i=0;i<26;++i) { son=p->ch[i]; if(son) { if(p==root)son->fail=p; else { tmp=p->fail; while(tmp) { if(tmp->ch[i]) { son->fail=tmp->ch[i]; break; } tmp=tmp->fail; } if(!tmp)son->fail=root; } q[++tail]=son; } } }}void query(){ int len=strlen(s), v; node *p=root, *temp; for(int i=0;i<len;++i) { if(s[i]<'A'||s[i]>'Z') { p=root; continue; } v=s[i]-'A'; while(p) { if(p->ch[v]) { p=p->ch[v]; break; } p=p->fail; } if(!p)p=root; else { temp=p; while(temp) { if(temp->pos)++ans[temp->pos]; temp=temp->fail; } } }}int main(){ while(~scanf("%d",&n)) { root=cnt=tree; for(int i=1;i<=n;++i) { scanf("%s",w[i]); add(root,i); } bfs(); scanf("%s",s); query(); for(int i=1;i<=n;++i) if(ans[i]) { printf("%s: %d\n",w[i],ans[i]); ans[i]=0; } for(node *p=tree;p<=cnt;++p) p->init(); } return 0;
zoj3430
题目大意:给你一个加密规则:将所有字符写成二进制并串联起来。然后每6位数组成一个新的二进制数,再转化为十进制数,根据密码表翻译成字符。若len%3==1,就再加上=。若len%3==2,就加上==。
现在已知一些被加密后的模式串和一些加密后的母串。求每一个母串中出现了多少个模式串。
这道题巧妙地运用位运算可以很方便的还原字符串。要注意虽然密码串是从‘0’到‘z’,但原串是0到255。而且是多组数据,一定要清零。被坑惨了…
#include <iostream>#include <cstdio>#include <cstring>#define MAXN 50050using namespace std;char w[MAXN];bool vis[MAXN];int n, temp[MAXN], cnt;struct node{ int ch[256], fail, cnt; void init() { cnt=fail=0; memset(ch,0,sizeof ch); }}tree[MAXN];void add(int r){ for(int i=1;i<=temp[0];++i) { if(tree[r].ch[temp[i]])r=tree[r].ch[temp[i]]; else r=tree[r].ch[temp[i]]=++cnt; } tree[r].cnt=1;}int key[256];void table(){ for(int i=0;i<26;++i)key[i+'A']=i; for(int i=0;i<26;++i)key[i+'a']=i+26; for(int i=0;i<10;++i)key[i+'0']=i+52; key['+']=62, key['/']=63;}void change(char s[]){ temp[0]=0; int len, x=0; for(len=strlen(s);s[len-1]=='=';--len); for(int i=0, tmp=0;i<len;++i) { x=(x<<6)|key[s[i]], tmp+=6; if(tmp>=8) { temp[++temp[0]]=(x>>(tmp-8))&255; tmp-=8; } }}int head, tail, q[MAXN];void bfs(){ head=tail=0; q[++tail]=0; int p, tmp; while(head<tail) { p=q[++head]; for(int i=0;i<256;++i) { if(tree[p].ch[i]) { tmp=tree[p].ch[i]; if(p)tree[tmp].fail=tree[tree[p].fail].ch[i]; q[++tail]=tmp; } else tree[p].ch[i]=tree[tree[p].fail].ch[i]; } }}void query(int w[]){ int ans=0, p=0, tmp; for(int i=1;i<=w[0];++i) { p=tree[p].ch[w[i]]; tmp=p; while(vis[tmp]) { ans+=tree[tmp].cnt; vis[tmp]=0; tmp=tree[tmp].fail; } } printf("%d\n",ans);}int main(){ table(); while(~scanf("%d",&n)) { cnt=0; for(int i=1;i<=n;++i) { scanf("%s",w); change(w); add(0); } bfs(); scanf("%d",&n); while(n--) { memset(vis,1,sizeof vis); scanf("%s",w); change(w); query(temp); } for(int i=0;i<=cnt;++i) tree[i].init(); puts(""); } return 0;}
poj2778
题目大意:有
不得不承认蒟蒻是看了题解的…
首先对这
这样就是一个矩阵加速的问题了。
#include <iostream>#include <cstdio>#include <cstring>#define MAXN 110#define LL long long int#define mod 100000using namespace std;struct mat{ LL num[MAXN][MAXN], n; void init() { memset(num,0,sizeof num); n=0; } mat operator * (const mat &a)const { mat ans; ans.init(); ans.n=n; for(int i=0;i<=n;++i) for(int j=0;j<=n;++j) for(int k=0;k<=n;++k) ans.num[i][k]=(ans.num[i][k]+num[i][j]*a.num[j][k])%mod; return ans; }}ans;mat power(mat a,int pos){ mat ans=a; while(pos) { if(pos&1)ans=ans*a; a=a*a; pos>>=1; } return ans;}inline int getid(char a){ if(a=='A')return 0; if(a=='C')return 1; if(a=='G')return 2; return 3;}struct node{ int ch[5], fail, cnt; void init() { fail=cnt=0; memset(ch,0,sizeof ch); }}tree[MAXN];int cnt, root;char w[MAXN];void add(){ int len=strlen(w), r=root, v; for(int i=0;i<len;++i) { v=getid(w[i]); if(tree[r].ch[v])r=tree[r].ch[v]; else r=tree[r].ch[v]=++cnt; } ++tree[r].cnt;}int q[MAXN], head, tail;void bfs(){ head=tail=0; q[++tail]=0; int p, tmp; while(head<tail) { p=q[++head]; for(int i=0;i<4;++i) { if(tree[p].ch[i]) { tmp=tree[p].ch[i]; if(p) { tree[tmp].fail=tree[tree[p].fail].ch[i]; tree[tmp].cnt+=tree[tree[tree[p].fail].ch[i]].cnt; } q[++tail]=tmp; } else tree[p].ch[i]=tree[tree[p].fail].ch[i]; } }}void build(){ ans.init(); for(int i=0;i<=cnt;++i) { for(int j=0;j<4;++j) { if(tree[tree[i].ch[j]].cnt)continue; ++ans.num[i][tree[i].ch[j]]; } } ans.n=cnt;}int n, m;LL out;int main(){ while(~scanf("%d%d",&n,&m)) { root=cnt=0; for(int i=0;i<n;++i) { scanf("%s",w); add(); } bfs(); build(); ans=power(ans,m-1); out=0; for(int i=0;i<=cnt;++i)out=(out+ans.num[0][i])%mod; printf("%d\n",out); for(int i=0;i<=cnt;++i)tree[i].init(); } return 0;}
hdu2243
这道题就是上面那道的加强版。
蒟蒻目前TLE中,期待持续更新…
- 【AC自动机】hdu2222 hdu2896 hdu3065 zoj3430 poj2778 hdu2243
- 【AC自动机】hdu2222 hdu2896 hdu3065 zoj3430 poj2778 hdu2243
- 【AC自动机】hdu2222 hdu2896 hdu3065 zoj3430 poj2778
- hdu2896 & hdu3065 ac自动机
- AC自动机 hdu2896 hdu3065 病毒侵袭
- AC自动机简单题 hdu2222+poj2778
- hdu2222&hdu3065 AC自动机模板题
- zoj3430之AC自动机
- zoj3430 AC自动机模拟
- ZOJ3430-AC自动机
- ZOJ3430 AC自动机
- HDU2243-AC自动机+矩阵
- POJ2778 AC自动机 + 矩阵
- poj2778 ac自动机+矩阵
- POJ2778-AC自动机+矩阵
- hdu3065 AC自动机
- hdu3065之AC自动机
- hdu3065(ac自动机)
- 网络抓取页面乱码
- Python爬虫学习系列教程
- android音乐播放器,可进度条调节
- Miller_Rabin 大素数判断
- C++ 右值引用
- 【AC自动机】hdu2222 hdu2896 hdu3065 zoj3430 poj2778 hdu2243
- 最大连续和(分治法)
- linux 每次关机后重启都要手动source /etc/profile
- 关于控件getWidth和getHeight方法都为0的解释及解决方法
- leetcode刷题日记——Linked List Cycle
- Java之美[从菜鸟到高手演变]系列之博文阅读导航
- Java中 FileReader的中文乱码问题
- Android实战之 Jmail发送邮件(无需跳转到邮件客户端)
- ViewPager