BZOJ2434 [Noi2011]阿狸的打字机
AC自动机+树状数组
先建出fail树,对于查询x在y中出现几次,就等于在x为根的子树下有多少个节点为单词y在tire树路径上所在的节点,可用dfs+树状数组离线求出答案。
#include<cstdio> #include<cstring> #include<queue> #include<set> #include<vector> #define pb push_back #define mp make_pair #define fi first #define sc second #define N 1000010 using namespace std; char s[N]; int tmp,len,i,fa[N],cnt,p[N],tot,fail[N],c,a,b,ans[N]; int C[N]; int f[N][27],ss,L[N],R[N],m; int dp,pre[N],tt[N],query[N]; queue<int> q; vector<pair<int,int> > vec[N]; int lowbit(int x) { return x&(-x); } void cc(int x,int w) { while (x<=ss) { C[x]+=w; x+=lowbit(x); } } int sum(int x) { int ans=0; while (x>0) { ans+=C[x]; x-=lowbit(x); } return ans; } void link(int x,int y) { dp++;pre[dp]=p[x];p[x]=dp;tt[dp]=y; } void dfs(int x) { int i=p[x]; ss++;L[x]=ss; while (i) { dfs(tt[i]); i=pre[i]; } R[x]=ss; } void solve() { int i,tmp=0,cnt=0,j; for (i=0;i<len;i++) if (s[i]=='B') { cc(L[tmp],-1); tmp=fa[tmp]; } else if (s[i]=='P') { cnt++; for (j=0;j<vec[cnt].size();j++) { int o=query[vec[cnt][j].fi]; ans[vec[cnt][j].sc]=sum(R[o])-sum(L[o]-1); } } else { tmp=f[tmp][s[i]-97]; cc(L[tmp],1); } } int main() { scanf("%s",s); len=strlen(s); tmp=0; for (i=0;i<len;i++) if (s[i]=='B') tmp=fa[tmp]; else if (s[i]=='P') { cnt++; query[cnt]=tmp; } else { if (f[tmp][s[i]-97]==0) { f[tmp][s[i]-97]=++tot; fa[tot]=tmp; } tmp=f[tmp][s[i]-97]; } for (i=0;i<26;i++) if (f[0][i]) { fail[f[0][i]]=0; q.push(f[0][i]); } while (!q.empty()) { c=q.front();q.pop(); for (i=0;i<26;i++) if (f[c][i]==0) f[c][i]=f[fail[c]][i]; else { fail[f[c][i]]=f[fail[c]][i]; q.push(f[c][i]); } } for (i=1;i<=tot;i++) link(fail[i],i); dfs(0); scanf("%d",&m); for (i=1;i<=m;i++) { scanf("%d%d",&a,&b); vec[b].pb(mp(a,i)); } solve(); for (i=1;i<=m;i++) printf("%d\n",ans[i]); }