【题解】
KMP算法:每次询问时求出x串的失配函数,然后在y串上匹配,总复杂度O(m*len) 可以得40分
如果把所有单词建成一棵字母树,考虑类似的暴力:
对每次询问,枚举y串的每个点(将这个点理解为x在y串上的最后一个匹配点),若从它沿失配指针到root的路径经过x串的最后一个点,则答案加1
由于(x1,y),(x2,y),…这些y相同的询问在AC自动机上走的路径一样,可以对于y一次全部求得,将询问统计后离线处理即可
做到这一步后,程序的时间主要浪费在哪里呢?发现对于每个y串,都要处理它的每个字符到root的路径,而其实许多串都有很长的公共前缀,这里重复计算了
可以反过来考虑:每个x串的最后一个点,沿失配指针逆向走,寻找所有y上的点
这时如果把x看成一棵树的树根,把失配指针看成树边,那问题就转化为求以x为根的子树中有多少点在y串上
因此,以失配指针为树边,将AC自动机建成一棵树(称fail树),只需要想办法把y上的所有点标在树上,并可以实现快速查询就行了
这样的话,若y2与y1有公共前缀,标记y2的点时公共前缀就不用重新操作了
快速查询子树中有多少标记点,可以联想到区间快速求和(树状数组)
而子树的确可以转化为一段连续的区间:
dfs整棵fail树,对每个结点记录进入、最后离开时间(L,R),则满足L<=L1<R1<=R的点(L1,R1)必在点(L,R)的子树中
因此y串每标记一个点t,就是将fail树对应数列的L[t]位+1,删除同理,询问时查询( L[x_last] , R[x_last] )的区间和就行了
【代码】
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<vector> using namespace std; vector<int> Ask[100005],Ans[100005],G[100005];//Ask[i]:所有y==i的询问对应的x char s[100005]={0}; int ch[100005][130]={0},ls[100005]={0},pre[100005]={0},L[100005]={0},R[100005]={0},c[200005]={0},f[100005]={0},q[100005]={0},x[100005]={0},y[100005]={0},p[100005]={0}; int len,u=0,sz=0,tot=0; void tj(char t) { if(ch[u][t]==0) { ch[u][t]=++sz; pre[ch[u][t]]=u; } u=ch[u][t]; } void getf() { int i,j,head=0,tail=0; for(i='a';i<='z';i++) if(ch[0][i]!=0) { q[tail++]=ch[0][i]; G[0].push_back(ch[0][i]); } while(head<tail) { for(i='a';i<='z';i++) if(ch[q[head]][i]!=0) { q[tail++]=ch[q[head]][i]; j=f[q[head]]; while(j!=0&&ch[j][i]==0) j=f[j]; f[ch[q[head]][i]]=ch[j][i]; G[ch[j][i]].push_back(ch[q[head]][i]); } head++; } } void dfs(int x) { int i; L[x]=++tot; for(i=0;i<G[x].size();i++) dfs(G[x][i]); R[x]=++tot; } void jia(int p,int x)//第p个位置的值加x { for(;p<=2*len;p+=(p&(-p))) c[p]+=x; } int cx(int p) { int sum=0; for(;p>0;p-=(p&(-p))) sum+=c[p]; return sum; } int main() { int m,i,j=0,k; scanf("%s",s); len=strlen(s); for(i=0;i<len;i++) { if(s[i]>='a'&&s[i]<='z') tj(s[i]); if(s[i]=='B') u=pre[u]; if(s[i]=='P') ls[++j]=u; } scanf("%d",&m); for(i=1;i<=m;i++) { scanf("%d%d",&x[i],&y[i]); Ask[y[i]].push_back(x[i]); } getf(); dfs(0); u=k=0; for(i=0;i<len;i++) { if(s[i]>='a'&&s[i]<='z') { u=ch[u][s[i]]; jia(L[u],1); } if(s[i]=='B') { jia(L[u],-1); u=pre[u]; } if(s[i]=='P') { k++; for(j=0;j<Ask[k].size();j++) Ans[k].push_back(cx(R[ ls[Ask[k][j]] ])-cx(L[ ls[Ask[k][j]] ]-1)); } } for(i=1;i<=m;i++) printf("%d\n",Ans[y[i]][p[y[i]]++]); return 0; }