【题解】2021HDU多校第十场 HDU7084 Pty loves string
2021HDU多校第十场 HDU7084 Pty loves string
题意
给一个长为\(n\)的串\(S\),共\(Q\)次询问,每次询问给定\(x,y\),求将\(S\)长为\(x\)的前缀和长为\(y\)的后缀拼接得到的新串在\(S\)中出现了多少次。\(T\)组数据。
\(1\le T \le 5,1\le n,Q\le 2\times 10^5,1\le x,y\le n\)
题解
记\(pre(i):=S[1...i],suf(i):=S[i...n]\),首先用\(KMP\)求出\(S\)所有前缀和所有后缀的\(border\),然后按照如下方式建立正串的\(border\ tree\):节点\(i\)表示\(pre(i)\),对于\(i=1..n\),从\(f_i\)向\(i\)连一条边,表示\(border(pre(i))\rightarrow pre(i)\)。这样,以节点\(u\)为根的子树的节点所代表的的串均以\(pre(u)\)为前缀。再对反串建\(border\ tree\),即以节点\(u\)为根的子树的节点所代表的的串均以\(suf(u)\)为前缀。一次询问\(x,y\)相当于询问有多少\(i\)满足\(pre(i)\in subtree1(pre(x))\)且\(suf(i+1)\in subtree2(suf(n-y+1))\)。可以转化为二维数点问题,记两棵树的dfs序数组分别为\(dfn1,dfn2\)。对第一颗树上的每一个点\(u\),令其权值为\(dfn2[u+1]\)。查询时,记\(y'=n-y+1\),相当于查询\(pre(x)\)的子树中有多少个点的权值在\([dfn2[y'],dfn2[y'+sz2[y']-1]]\)中。用主席树按\(dfn\)序预处理即可。
#include <bits/stdc++.h>
#define pb(x) emplace_back(x)
using namespace std;
const int N=2e5+10;
char S[N];
int f[N],g[N],n,Q;
int rt[N],gt=0,ls[N*40],rs[N*40],s[N*40];
#define mid ((l+r)>>1)
void ins(int &o,int pre,int l,int r,int x){
o=++gt;
s[o]=s[pre]+1;ls[o]=ls[pre];rs[o]=rs[pre];
if(l==r){return ;}
if(x<=mid)ins(ls[o],ls[pre],l,mid,x);
else ins(rs[o],rs[pre],mid+1,r,x);
}
int q(int o,int pre,int l,int r,int x,int y){
if(x<=l&&r<=y)return s[o]-s[pre];
int res=0;
if(x<=mid)res+=q(ls[o],ls[pre],l,mid,x,y);
if(y>mid)res+=q(rs[o],rs[pre],mid+1,r,x,y);
return res;
}
vector<int> e[N],e2[N];
int sz[N],sz2[N],dfn[N],dfn2[N],c1=0,a[N];
void dfs(int u){
sz[u]=1;dfn[u]=++c1;a[c1]=u;
for(auto v:e[u]){
dfs(v);
sz[u]+=sz[v];
}
}
void dfs2(int u){
sz2[u]=1;dfn2[u]=++c1;
for(auto v:e2[u]){
dfs2(v);
sz2[u]+=sz2[v];
}
}
void f1(){
for(int i=1;i<=gt;i++){s[i]=ls[i]=rs[i]=0;}
gt=0;
scanf("%d%d%s",&n,&Q,S+1);
int j=0;
f[0]=0;g[n+1]=0;
for(int i=2;i<=n;i++){
while(j&&S[j+1]!=S[i])j=f[j];
if(S[j+1]==S[i])++j;
f[i]=j;
}
g[n]=n+1;j=n+1;
for(int i=n-1;i>=1;i--){
while(j<n+1&&S[j-1]!=S[i])j=g[j];
if(S[j-1]==S[i])--j;
g[i]=j;
}
for(int i=1;i<=n;i++){
e[f[i]].pb(i);e2[g[i]].pb(i);
}
c1=0;dfs(0);
c1=0;dfs2(n+1);
for(int i=1;i<=n+1;i++){
ins(rt[i],rt[i-1],1,n+1,dfn2[a[i]+1]);
}
while(Q--){
int x,y;scanf("%d%d",&x,&y);y=n-y+1;
printf("%d\n",q(rt[dfn[x]+sz[x]-1],rt[dfn[x]-1],1,n+1,dfn2[y],dfn2[y]+sz2[y]-1));
}
for(int i=0;i<=n+1;i++){e[i].clear();e2[i].clear();}
}
int main(){
int t;scanf("%d",&t);
while(t--)
f1();
return 0;
}