[bzoj2434][Noi2011]阿狸的打字机——AC自动机

题目大意:

给定n个字符串,每次询问第x个字符串在第y个字符串中出现了多少次。

思路:

显然我们需要先把AC自动机给建出来。
考虑如何最暴力地计算第x个字符在第y个字符中出现了多少次,我们可以在Trie上暴力跳y的每一个节点,然后对于y的每一个节点跳fail,如果跳到了x串的结尾,那么答案+1。
这样对于每一个节点都跳fail显然复杂度无法接受,那么换一个角度考虑,x的结尾可以被y串上的多少个节点给跳到。
于是我们可以把每个节点的fail看成是它的父亲,然后问题就转化为了求x的结尾点的子树中包含了多少个y的节点,这样以后可以暴力标记y上的每一个节点,对于x直接子树求和即可。
这样以后,对于y相同的二元组就可以同时处理了,相当于把x挂在了y上。
但是这样复杂度还是太高,无法接受,于是我们发现复杂度主要在暴力标记y上的每一个节点上,于是考虑将询问按照y离线,把y挂在串y的结尾点上,然后按照dfs的顺序遍历Trie树上的每一个节点,进入这个节点时打上标记,回溯时将标记撤回,这样到了y的结尾的时候,不难发现此时打上标记的点恰好是y串上所有的点。

#include<bits/stdc++.h>

#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
#define debug(x) cout<<#x<<"="<<x<<" "
#define pii pair<int,int>
#define fi first
#define se second
#define mk make_pair
#define pb push_back
typedef long long ll;

using namespace std;

void File(){
	freopen("bzoj2434.in","r",stdin);
	freopen("bzoj2434.out","w",stdout);
}

template<typename T>void read(T &_){
	_=0; T f=1; char c=getchar();
	for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
	for(;isdigit(c);c=getchar())_=(_<<1)+(_<<3)+(c^'0');
	_*=f;
}

const int maxn=1e5+10;
int m;

int ch[maxn][26],num[maxn],fail[maxn],cnt;
int fa[maxn],le[maxn],pos[maxn];
vector<int>rank[maxn];

void print(int u){
	if(fa[u]!=1)print(fa[u]);
	putchar(le[u]+'a');
}

void init(){
	fail[cnt=1]=1;

	char s[maxn];
	scanf("%s",s+1);

	int len=strlen(s+1),u=1,c,cnt_rank=0;
	REP(i,1,len){
		if(s[i]=='B')u=fa[u];
		else if(s[i]=='P'){
			++num[u];
			rank[u].pb(++cnt_rank);
			pos[cnt_rank]=u;
		}
		else{
			c=s[i]-'a';
			if(!ch[u][c])ch[u][c]=++cnt;
			fa[ch[u][c]]=u,le[ch[u][c]]=c;
			u=ch[u][c];
		}
	}
}

void build_fail(){ int h=1,t=0,q[maxn];
	REP(i,0,25)if(ch[1][i]){
		fail[ch[1][i]]=1;
		q[++t]=ch[1][i];
	}

	while(h<=t){
		int u=q[h++];
		REP(i,0,25)if(ch[u][i]){
			int v=ch[u][i],p=fail[u];
			q[++t]=v;
			while(p!=1 && !ch[p][i])p=fail[p];
			if(ch[p][i])fail[v]=ch[p][i];
			else fail[v]=1;
		}
	}
}

int beg[maxn],to[maxn],las[maxn],cnte,dfn[maxn],cnt_dfn,sz[maxn];

void add(int u,int v){
	las[++cnte]=beg[u]; beg[u]=cnte; to[cnte]=v;
}

void dfs_dfn(int u){
	dfn[u]=++cnt_dfn;
	sz[u]=1;
	for(int i=beg[u];i;i=las[i]){
		dfs_dfn(to[i]);
		sz[u]+=sz[to[i]];
	}
}

struct BIT{
	int sum[maxn];
	int lowbit(int x){
		return x&(-x);
	}
	void modify(int p,int x){
		for(;p<=cnt;p+=lowbit(p))sum[p]+=x;
	}
	int query(int p){
		int ret=0;
		for(;p>=1;p-=lowbit(p))ret+=sum[p];
		return ret;
	}
}T;

int ans[maxn];
vector<pii>qu[maxn];

void solve(int u){
	T.modify(dfn[u],1);

	REP(i,0,rank[u].size()-1){
		int y=rank[u][i];
		REP(j,0,qu[y].size()-1){
			int x=pos[qu[y][j].fi];
			ans[qu[y][j].se]=T.query(dfn[x]+sz[x]-1)-T.query(dfn[x]-1);
		}
	}

	REP(i,0,25)if(ch[u][i]){
		int v=ch[u][i];
		solve(v);
	}
	T.modify(dfn[u],-1);
}

int main(){
	File();

	init();

	build_fail();

	REP(i,2,cnt)add(fail[i],i);
	dfs_dfn(1);

	int x,y;
	read(m);
	REP(i,1,m){
		read(x),read(y);
		qu[y].pb(mk(x,i));
	}

	solve(1);
	REP(i,1,m)printf("%d\n",ans[i]);

	return 0;
}

posted @ 2018-12-26 21:54  ylsoi  阅读(132)  评论(0编辑  收藏  举报