[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;
}