【题解】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;
}
posted @ 2021-08-19 19:45  bobh  阅读(187)  评论(0编辑  收藏  举报