BZOJ3881: [Coci2015]Divljak
来源:互联网 发布:数据库设计实例 教材 编辑:程序博客网 时间:2024/04/24 23:40
BZOJ3881: [Coci2015]Divljak
AC自动机·Fail树
题解:
一开始想错了,还是对AC自动机的性质太不熟悉了。。。
以前没接触过Fail树,先来简单总结一下。
Fail树就是只保留AC自动机的fail指针并将其反向建出来的树。
对于Fail树上的节点,它代表的字符串是其子树中的节点代表的字符串的后缀。也就是下面的包含上面的。
考虑这样一个问题:假设给你若干个字符串,每次询问a在b中出现了多少次?可以把字符串建AC自动机,建Fail树,让b在AC自动机上走,走到的点+1,最后查询一下a在Fail树上的子树和即可。处理下一个询问之前别忘了把+1的点清零。
这道题的不同在于,询问是a在几个b中出现过,就是说a在一个b中出现很多次也只能贡献1。因此可以让b在AC自动机上走,走到的点+1,然后把走到的点按dfs序排序,相邻两个的lca处-1。查询a的子树和即是答案。
Code:
#include <algorithm>#include <iostream>#include <cstring>#include <cstdio>#include <queue>#define D(x) cout<<#x<<" = "<<x<<" "#define E cout<<endlusing namespace std;const int N = 2000005;int n,q; char str[N]; int tp[N];int ch[N][26],fail[N],ptr[N],sz=1;int pa[N][25],dep[N],pos[N],end[N],tim;int insert(char s[]){ int x=1; for(int i=0;s[i]!='\0';i++){ if(!ch[x][s[i]-'a']){ ch[x][s[i]-'a']=++sz; } x=ch[x][s[i]-'a']; } return x;}void acBuild(){ queue<int> q; q.push(1); int f; while(!q.empty()){ int u=q.front(); q.pop(); for(int c=0;c<26;c++){ if(ch[u][c]){ int v=ch[u][c]; for(f=fail[u];f && !ch[f][c];f=fail[f]); if(f) fail[v]=ch[f][c]; else fail[v]=1; q.push(v); } else{ ch[u][c]=ch[fail[u]][c]?ch[fail[u]][c]:1; } } }}struct Edge{ int to,next;} e[N*2];int head[N], ec;void add(int a,int b){// D(a); D(b); E; ec++; e[ec].to=b; e[ec].next=head[a]; head[a]=ec;}void dfs(int u,int f){ pa[u][0]=f; dep[u]=dep[f]+1; pos[u]=++tim; for(int i=head[u];i;i=e[i].next){ int v=e[i].to; if(v==f) continue; dfs(v,u); } end[u]=tim;}void initLCA(){ for(int j=1;j<=21;j++){ for(int i=1;i<=sz;i++){ pa[i][j]=pa[pa[i][j-1]][j-1]; } }}int LCA(int a,int b){ if(dep[a]<dep[b]) swap(a,b); int cha=dep[a]-dep[b]; for(int j=21;j>=0;j--){ if(cha&(1<<j)) a=pa[a][j]; } if(a!=b){ for(int j=21;j>=0;j--){ if(pa[a][j]!=pa[b][j]){ a=pa[a][j]; b=pa[b][j]; } } a=pa[a][0]; } return a;}void failBuild(){ for(int i=2;i<=sz;i++) add(fail[i],i); dfs(1,0); initLCA();// for(int i=1;i<=sz;i++) D(i), D(pos[i]), E;}struct BIT{ int c[N], sz; void init(int _sz){ sz=_sz; memset(c,0,sizeof(c)); } void add(int x,int d){ while(x<=sz){ c[x]+=d; x+=x&(-x); } } int sum(int x){ int ans=0; while(x){ans+=c[x];x-=x&(-x);} return ans; } int sum(int x,int y){ return sum(y)-sum(x-1); }} bit;bool cmp(int a,int b){ return pos[a]<pos[b];}void work(char s[]){ int x=1; int tpsz=0; tp[tpsz++]=1; for(int i=0;s[i]!='\0';i++){ x=ch[x][s[i]-'a']; tp[tpsz++]=x; } sort(tp,tp+tpsz,cmp); tpsz=unique(tp,tp+tpsz)-tp;// D(tpsz); E;// for(int i=0;i<tpsz;i++){ D(tp[i]); E; } for(int i=0;i<tpsz;i++){ bit.add(pos[tp[i]],1); if(i) bit.add(pos[LCA(tp[i-1],tp[i])],-1); }}void solve(){ bit.init(sz); cin>>q; int op,p; while(q--){// D(q); E; scanf("%d",&op); if(op==1){ scanf("%s",str); work(str); } else{ scanf("%d",&p); int l=pos[ptr[p]], r=end[ptr[p]];// D(l); D(r); E; printf("%d\n",bit.sum(l,r)); } }}int main(){ freopen("a.in","r",stdin); cin>>n; for(int i=1;i<=n;i++){ scanf("%s",str); ptr[i]=insert(str); }// D(sz); E; acBuild();// for(int i=1;i<=sz;i++) D(i), D(fail[i]), E; failBuild(); solve();}
阅读全文
0 0
- 【COCI2015】bzoj3881 Divljak
- BZOJ3881: [Coci2015]Divljak
- [BZOJ3881][Coci2015]Divljak(AC自动机+dfs序+lca+bit)
- 【AC自动机-fail树+树链合并】BZOJ3881(Coci2015)[Divljak]题解
- [BZOJ3881]-[Coci2015]Divljak-AC自动机+fail树+dfs序前缀和
- BZOJ Coci2015 Divljak
- BZOJ 3881 [Coci2015]Divljak
- BZOJ 3881: [Coci2015]Divljak
- 【BZOJ】【P3881】【Coci2015】【Divljak】【题解】【AC自动机】
- BZOJ 3881 Coci2015 Divljak fail树+树链的并
- 3881: [Coci2015]Divljak fail树+树链的并
- [AC自动机 fail树 树链的并] BZOJ 3881 [Coci2015]Divljak
- bzoj 3881: [Coci2015]Divljak (AC自动机+容斥原理+LCA+树状数组)
- bzoj 3881: [Coci2015]Divljak AC自动机+树链的并+树状数组
- bzoj3881(ac自动机)
- [Coci2015]Stanovi解题报告
- bzoj3745: [Coci2015]Norma
- 【COCI2015】【BZOJ3810】Stanovi
- scrapy安装出错总结
- oracle时间分钟格式注意事项
- 食物链
- ABBYY Cup 3.0 E3
- JavaBean的设计原则
- BZOJ3881: [Coci2015]Divljak
- 易宝支付基于Kubernetes的私有容器云从0到1的建设之路
- 判断是否离开当前页面
- epoll边缘触发(epoll et) 源代码例子
- JavaScript Math
- QT设置界面大小
- 无限轮播
- java Collections工具类
- 在opencv3中的机器学习算法