P2414 [NOI2011]阿狸的打字机 AC自动机

题意

给定n个模式串,有m个询问,每次询问第X个模式串在第Y个模中出现了多少次

解题思路

以fail树相反的方向建一棵树T,问题转化为X的子树中有多少个y的终止节点。跑出T的dfs序,X的子树就可以表示为一段区间,然后对trie跑一遍dfs,每进入一个点就把dfs序列上对应的位置+1,离开一个点就把dfs序列上对应的位置-1,遍历到y的终止节点就计算所有Y=y的询问的结果,计算方法就是区间求和,用树状数组或线段树可以轻易地维护。

AC代码

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int maxn=2e5+5;
char s[maxn];
struct Query{
	int x,y,id,ans;
	bool operator<(const Query& b)const{return y<b.y;}
}q[maxn];
int ql[maxn],qr[maxn],pos[maxn];



//BIT
int c[maxn];
inline int lowbit(int x){return x&(-x);}
void add(int x,int d){for(;x<maxn-5;x+=lowbit(x))c[x]+=d;}
int getsum(int x){int res=0;for(;x;x-=lowbit(x))res+=c[x];return res;}
int getsum(int l,int r){return getsum(r)-getsum(l-1);}



//AC_Automaton
//root=0,range[1,tot]
const static int SIZE=26; 
int tr[maxn][SIZE],fail[maxn],tot;
int fa[maxn],val[maxn],ch[maxn][SIZE];
void insert(){
	int p=0,cnt=0;
	for(int i=0;s[i];i++){
		if(s[i]=='P'){pos[++cnt]=p;val[p]=cnt;}
		else if(s[i]=='B')p=fa[p];
		else{
			if(!tr[p][s[i]-'a'])tr[p][s[i]-'a']=++tot;
			fa[tr[p][s[i]-'a']]=p;
			p=tr[p][s[i]-'a'];
		}
	}
	for(int i=0;i<=tot;i++)for(int j=0;j<26;j++)ch[i][j]=tr[i][j];
}
void getfail(){
	queue<int>q;
	for(int i=0;i<SIZE;i++)if(tr[0][i])fail[tr[0][i]]=0,q.push(tr[0][i]);
	while(!q.empty()){
		int p=q.front();q.pop();
		for(int i=0;i<SIZE;i++){
			if(tr[p][i]){
				fail[tr[p][i]]=tr[fail[p]][i],q.push(tr[p][i]);
			}
			else tr[p][i]=tr[fail[p]][i];
		}
	}
}




//inv fail tree
struct Edge{
	int v,nxt;
}e[maxn];
int fi[maxn],se[maxn],sz;
int head[maxn],num;
void init_graph(){
	memset(head,0,sizeof(head));
	num=1;
}
void addedge(int u,int v){e[num].v=v;e[num].nxt=head[u];head[u]=num++;}
void dfs_fail(int p){
	fi[p]=++sz;
	for(int i=head[p];i;i=e[i].nxt)dfs_fail(e[i].v);
	se[p]=sz;	
}



//get ans
int Ans[maxn];
void dfs_trie(int p){
	add(fi[p],1);
	if(val[p]){
		for(int i=ql[val[p]];i<=qr[val[p]];i++)
			q[i].ans=getsum(fi[pos[q[i].x]],se[pos[q[i].x]]);
	}
	for(int i=0;i<26;i++)if(ch[p][i])dfs_trie(ch[p][i]);
	add(fi[p],-1);
}

int main()
{
//#ifndef ONLINE_JUDGE
//	freopen("in.txt","r",stdin);
//#endif
	scanf("%s",s);
	insert();
	
	
	getfail();
	init_graph();
	for(int i=1;i<=tot;i++)addedge(fail[i],i);
	sz=0;dfs_fail(0);//建inv fail tree,跑出dfs序 
	
	
	
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d %d",&q[i].x,&q[i].y);
		q[i].id=i;q[i].ans=0;
	}
	sort(q+1,q+1+n);
	for(int i=1,t=1;i<=n;i++){
		ql[q[i].y]=i;
		while(q[t+1].y==q[i].y)t++;
		qr[q[i].y]=t;i=t;
	}
	dfs_trie(0);//遍历trie,跑出答案 
	
	for(int i=1;i<=n;i++)Ans[q[i].id]=q[i].ans;
	for(int i=1;i<=n;i++)printf("%d\n",Ans[i]);
    return 0;
}

posted @ 2019-08-27 02:15  _Backl1ght  阅读(134)  评论(0编辑  收藏  举报