洛谷 P2414 [NOI2011] 阿狸的打字机(AC自动机、树状数组)

传送门


解题思路

被细节卡了一上午wwww

很好地一道题,考察了AC自动机的本质。

先考虑如何暴力做:枚举y中的每一位,并不断跳fail,若跳到了x,则ans++。

如何优化这个过程呢?

先离线根据y从小到大排序。
当我们以0为根节点,fail[now]向now连边时,AC自动机就变成了一颗树。这时我们把上述问题放在树上,就变成了询问以x的结尾为根的子树里有多少位y。

可以用dfs序+树状数组/线段树实现。

具体建AC自动机过程:

  • B:now-->fa[now]
  • P:打标记
  • 字符:继续走。

求解过程:

  • B: dfn[now]对应的点权-1,now-->fa[now]
  • P:回答询问,查询区间和。
  • 字符:继续走,并使dfn[now]对应的点权+1

AC代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
using namespace std;
const int maxn=1e5+5;
int n,m,cnt,p[maxn],siz[maxn],dfn[maxn],times,fa[maxn],num[maxn],tr[maxn][30],fail[maxn],tot,d[maxn];
string s;
struct node{
	int v,next;
}e[maxn];
struct Node{
	int x,y,id,ans;
}q[maxn];
bool cmp1(Node a,Node b){
	return a.y<b.y;
}
bool cmp2(Node a,Node b){
	return a.id<b.id;
}
void insert(int u,int v){
	cnt++;
	e[cnt].v=v;
	e[cnt].next=p[u];
	p[u]=cnt;
}
void dfs(int u){
	siz[u]=1;
	dfn[u]=++times;
	for(int i=p[u];i!=-1;i=e[i].next){
		int v=e[i].v;
		dfs(v);
		siz[u]+=siz[v];
	}
}
void inserts(){
	int len=s.length(),now=0;
	for(int i=0;i<len;i++){
		if(s[i]=='B') now=fa[now];
		else if(s[i]=='P') num[++m]=now;
		else{
			if(tr[now][s[i]-'a']) now=tr[now][s[i]-'a'];
			else{
				tr[now][s[i]-'a']=++tot;
				fa[tot]=now;
				now=tot;
			}
		}
	}
}
void build(){
	queue<int> q;
	for(int i=0;i<26;i++){
		if(tr[0][i]) q.push(tr[0][i]),insert(0,tr[0][i]);
	}
	while(!q.empty()){
		int now=q.front();q.pop();
		for(int i=0;i<26;i++){
			if(tr[now][i]) fail[tr[now][i]]=tr[fail[now]][i],q.push(tr[now][i]),insert(fail[tr[now][i]],tr[now][i]);
			else tr[now][i]=tr[fail[now]][i];
		}
	}
}
inline int lowbit(int x){
	return x&(-x); 
}
void update(int x,int v){
	for(int i=x;i<maxn;i+=lowbit(i)) d[i]+=v;
}
int query(int x){
	int res=0;
	for(int i=x;i>=1;i-=lowbit(i)) res+=d[i];
	return res; 
}
void work(){
	int len=s.length(),now=0,cntq=1;
	for(int i=0;i<len;i++){
		if(s[i]=='B'){
			update(dfn[now],-1);
			now=fa[now];
		}
		else if(s[i]=='P'){
			while(cntq<=n&&num[q[cntq].y]==now){
				int x1=query(dfn[num[q[cntq].x]]+siz[num[q[cntq].x]]-1),x2=query(dfn[num[q[cntq].x]]-1);
				q[cntq].ans=x1-x2;
				cntq++;
			}
		}
		else{
			now=tr[now][s[i]-'a'];
			update(dfn[now],1);
		}
	}
}
int main(){
	ios::sync_with_stdio(false);
	memset(p,-1,sizeof(p));
	cin>>s;
	inserts();
	build();
	dfs(0); 
	cin>>n;
	for(int i=1;i<=n;i++){
		q[i].id=i;
		cin>>q[i].x>>q[i].y;
	}
	sort(q+1,q+n+1,cmp1);
	work();
	sort(q+1,q+n+1,cmp2);
	for(int i=1;i<=n;i++) cout<<q[i].ans<<endl;
	return 0;
}
posted @ 2021-10-12 14:56  尹昱钦  阅读(41)  评论(0编辑  收藏  举报