[AC自动机][树状数组]luogu P2414 阿狸的打字机
https://www.luogu.org/problem/P2414
分析
容易发现询问串A属于串B多少次就是问访问串B时,跳完每个点的fail链,到达串A末尾的次数
可以发现如果把fail树建出来,就相当于每到达一个点,就往fail树里面权值+1,离开时-1,当到达串B末尾时,询问串A末尾在fail树中子树的权值和
用DFS序和树状数组可以轻松解决
#include <iostream> #include <cstdio> #include <cstring> #include <queue> #include <algorithm> #define lowbit(x) x&-x using namespace std; const int N=2e5+10; struct Graph { int v,nx; }g[N]; int gcnt,list[N]; int fail[N],t[N][26],cnt,v[N]; struct Query { int x,y,id; }q[N]; int m,len,tme,p; int l[N],r[N],end[N],ans[N],f[N]; char c[N]; void Add(int u,int v) { g[++gcnt]=(Graph){v,list[u]};list[u]=gcnt; } bool CMP(Query a,Query b) { return a.y<b.y; } void Insert() { int x=0,id=0; for (int i=1,n=strlen(c+1);i<=n;i++) { if (c[i]=='B') { x=f[x]; continue; } if (c[i]=='P') { end[++id]=x; continue; } if (!t[x][c[i]-'a']) f[t[x][c[i]-'a']=++cnt]=x; x=t[x][c[i]-'a']; } } void Build() { queue<int> q; while (!q.empty()) q.pop(); for (int i=0;i<26;i++) if (t[0][i]) q.push(t[0][i]),Add(0,t[0][i]); while (!q.empty()) { int u=q.front();q.pop(); for (int i=0;i<26;i++) if (t[u][i]) { int now=fail[u]; while (now&&!t[now][i]) now=fail[now]; fail[t[u][i]]=t[now][i];Add(t[now][i],t[u][i]); q.push(t[u][i]); } } } void DFS(int u) { l[u]=++tme; for (int i=list[u];i;i=g[i].nx) DFS(g[i].v); r[u]=tme; } void Addc(int x,int c) { for (;x<=tme;x+=lowbit(x)) v[x]+=c; } int Sum(int x) { int ans=0; for (;x;x-=lowbit(x))ans+=v[x]; return ans; } void Trie() { int x=0; for (int i=1,n=strlen(c+1);i<=n;i++) { if (c[i]=='B') { Addc(l[x],-1);x=f[x]; continue; } if (c[i]=='P') { while (end[q[p].y]==x) ans[q[p].id]=Sum(r[end[q[p].x]])-Sum(l[end[q[p].x]]-1),p++; continue; } x=t[x][c[i]-'a'];Addc(l[x],1); } } int main() { scanf("%s",c+1); Insert(); scanf("%d",&m); for (int i=1;i<=m;i++) scanf("%d%d",&q[i].x,&q[i].y),q[i].id=i; sort(q+1,q+m+1,CMP); Build(); DFS(0); p=1;len=0;Trie(); for (int i=1;i<=m;i++) printf("%d\n",ans[i]); }
在日渐沉没的世界里,我发现了你。