不难想到把(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;
}