loj6626 幼儿园唱歌题

题目

不难想到把(S)的反串(S^R)接到(S)后面,这样就可以把(S[l_1,r_1])的前缀转化为(S^R[n-r_1+1,n-l_1+1])的后缀

回文树上两节点的lca就是两个前缀的最长公共回文后缀,于是建出回文树来跑个lca就好了

之后这个lca可能太长了,长度超过了(min(r_2-l_2+1,r_1-l_1+1)),于是我们再倍增往上跳一跳使得长度更小一些就好了

注意把两个串拼起来的时候要插两个特殊字符

代码

1#include<bits/stdc++.h>
#define re register
#define min(a,b) ((a)<(b)?(a):(b))
inline int read() {
	char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar();
	while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
const int maxn=4e5+7;char S[maxn];
int fa[maxn],son[maxn][28],len[maxn],cnt,lst,pos[maxn],lg[maxn],dep[maxn];
int f[20][maxn],n,Q;
inline void ins(int c,int n) {
	int F=lst;
	while(S[n-len[F]-1]!=S[n]) F=fa[F];
	if(!son[F][c]) {
		int p=++cnt,k=fa[F];len[p]=len[F]+2;
		while(S[n-len[k]-1]!=S[n]) k=fa[k];
		fa[p]=son[k][c];son[F][c]=p;dep[p]=dep[fa[p]]+1;f[0][p]=fa[p];
	    for(re int i=1;i<=lg[dep[p]];++i) f[i][p]=f[i-1][f[i-1][p]];
	}
	lst=son[F][c];pos[n]=lst;
}
inline int LCA(int x,int y) {
	if(dep[x]<dep[y]) std::swap(x,y);
	for(re int i=lg[dep[x]];i>=0;--i)
		if(dep[f[i][x]]>=dep[y]) x=f[i][x];
	if(x==y) return x;
	for(re int i=lg[dep[x]];i>=0;--i)
		if(f[i][x]!=f[i][y]) x=f[i][x],y=f[i][y];
	return fa[x];
}
inline int get(int x,int l) {
	if(len[x]<=l) return len[x];
	for(re int i=lg[dep[x]];i>=0;--i)
		if(len[f[i][x]]>l) x=f[i][x];
	return len[fa[x]];
}
int main() {
	n=read();Q=read();scanf("%s",S+1);S[0]=-1;for(re int i=1;i<=n;i++) S[i]-='a';
	S[n+1]=26,S[n+2]=27;for(re int i=1;i<=n;i++) S[n+i+2]=S[n-i+1];
	for(re int i=2;i<=n+n+2;i++) lg[i]=lg[i>>1]+1;
	len[1]=-1,fa[0]=1;cnt=1;dep[1]=1;dep[0]=2;
	for(re int i=1;i<=n+n+2;i++) ins(S[i],i);
	for(re int x,y,l1,l2,r1,r2;Q;Q--) {
		l1=read(),r1=read(),l2=read(),r2=read();
		x=pos[r2],y=pos[n+2+n-l1+1];int t=LCA(x,y);
		printf("%d
",-1*get(t,min(r1-l1+1,r2-l2+1)));
	}
	return 0;
}
原文地址:https://www.cnblogs.com/asuldb/p/12005042.html