2021HDU多校第十场 HDU7084 Pty loves string
题意
给一个长为(n)的串(S),共(Q)次询问,每次询问给定(x,y),求将(S)长为(x)的前缀和长为(y)的后缀拼接得到的新串在(S)中出现了多少次。(T)组数据。
(1le T le 5,1le n,Qle 2 imes 10^5,1le x,yle 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)) ightarrow 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)))。可以转化为二维数点问题,对第一颗树上的每一个点(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
",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;
}