您的位置:首页 > 大数据 > 人工智能

bzoj2434 [Noi2011]阿狸的打字机(AC自动机+fail树+树状数组)

2018-03-23 11:43 295 查看
多次询问一个串x在另一个串y中的出现次数。

我们先建出ACAM,考虑如何做这件事,就是询问y有多少个节点沿着fail指针能找到x。但是这样太慢,我们不妨反向思考,就是求x沿着反向的fail指针能找到几个y的节点。

我们建出fail树(fail指针没有环 每个节点只有一个出度 那么反向之后显然是一棵树 x沿着反向的fail指针所能到达的节点就是x所在的子树)。

所以现在就是询问x的子树中有多少个y的节点。我们可以求出dfs序,转化为区间求和问题。

由于这道题的特殊性质,加入的字符串顺序是有规律的,我们可以把询问离线,按y从小到大排序,然后扫一遍串S来更新BIT,假设我们现在在节点p。

对于B:在BIT中删掉节点p,返回到fa[p]

对于P:出现了一个新的字符串tot,处理所有y=tot的询问。

对于加字符:走到对应节点p,在BIT中加入节点p。

复杂度O(nlogn)

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
#define ll long long
#define N 100010
#define inf 0x3f3f3f3f
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x*f;
}
int n=0,rt,son
[26],fail
,h
,num=1,fa
,id
,m,nn=0;
int in
,out
,dfn=0,c
,ans
;
char s
;
struct edge{
int to,next;
}data
;
struct quer{
int x,y,id;
}qq
;
inline bool cmp(quer a,quer b){return a.y<b.y;}
inline void adde(int x,int y){
data[++num].to=y;data[num].next=h[x];h[x]=num;
}
inline void add(int x,int val){for(;x<=n;x+=x&-x) c[x]+=val;}
inline int ask(int x){int res=0;for(;x;x-=x&-x) res+=c[x];return res;}
inline void buildTrie(){
int p=rt=++n,len=strlen(s+1);
for(int i=0;i<26;++i) son[0][i]=rt;
for(int i=1;i<=len;++i){
if(s[i]=='B'){p=fa[p];continue;}
if(s[i]=='P'){id[++nn]=p;continue;}
int &y=son[p][s[i]-'a'];if(!y) y=++n;fa[y]=p;p=y;
}
}
inline void buildAC(){
queue<int>q;q.push(rt);
while(!q.empty()){
int x=q.front();q.pop();
for(int i=0;i<26;++i){
int &y=son[x][i];
if(!y){y=son[fail[x]][i];continue;}q.push(y);
fail[y]=son[fail[x]][i];adde(fail[y],y);
}
}
}
void dfs(int x){
in[x]=++dfn;
for(int i=h[x];i;i=data[i].next) dfs(data[i].to);
out[x]=dfn;
}
int main(){
//  freopen("a.in","r",stdin);
scanf("%s",s+1);buildTrie();buildAC();dfs(1);m=read();
for(int i=1;i<=m;++i) qq[i].x=read(),qq[i].y=read(),qq[i].id=i;
sort(qq+1,qq+m+1,cmp);int len=strlen(s+1),now=1,p=rt,tot=0;
for(int i=1;i<=len;++i){
if(s[i]=='B'){add(in[p],-1);p=fa[p];continue;}
if(s[i]=='P'){
++tot;while(now<=m&&qq[now].y==tot){
int x=id[qq[now].x];ans[qq[now].id]=ask(out[x])-ask(in[x]-1);++now;
}continue;
}p=son[p][s[i]-'a'];add(in[p],1);
}for(int i=1;i<=m;++i) printf("%d\n",ans[i]);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: