不会manacher的先去学一下。
首先加入特殊字符,用manacher处理一遍,让pi代表以i为中心的回文串半径(不包括i本身)。
假设某个双回文串的两个回文中心为i、j (i<j) ,那么有pi+pj>=j-i-1。
化简一下,得到i+pi>=j-pj-1。
经过计算,我们知道,在添加特殊字符的串里面,这个回文串的长度为(j-i-1)*2+2=2(j-i)。
那么在原串中,这个双回文串的长度怎么算呢?
考虑一下特殊字符的出现位置、次数,可以得到是j-i。
于是i一定时,就要让j尽量大。
枚举i,这时i+pi就确定了,只需求出最大的j使j-pj-1<=i+pi。
于是需要一个数据结构,可以求出在某个区间中,小于某个值且最靠右的数在哪里。我写了一个奇怪的线段树,不知道有没有更好的方法。
代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ele int
using namespace std;
#define maxn 200010
struct node{
ele _min,_max;
node *l,*r;
}pool[maxn<<2];
char s1[maxn],s[maxn];
ele n,cnt,p[maxn];
node *root;
node *build(ele l,ele r){
node *q=&pool[cnt++];
if (l==r){
q->_min=q->_max=(l-1)-p[l-1]-1;
q->l=q->r=NULL;
}
else{
ele mid=(l+r)>>1;
q->l=build(l,mid);
q->r=build(mid+1,r);
q->_min=min(q->l->_min,q->r->_min);
q->_max=max(q->l->_max,q->r->_max);
}
return q;
}
ele query(node *x,ele a,ele b,ele l,ele r,ele k){
ele mid=(a+b)>>1;
if (l<=a && b<=r){
if (x->_min>k) return -1;
if (a==b) return a-1;
if (x->r->_min<k) return query(x->r,mid+1,b,l,r,k);
return query(x->l,a,mid,l,r,k);
}
ele lans=-1,rans=-1;
if (l<=mid) lans=query(x->l,a,mid,l,r,k);
if (mid<r) rans=query(x->r,mid+1,b,l,r,k);
if (rans!=-1) return rans;
return lans;
}
int main(){
scanf("%s",s1);
n=strlen(s1);
s[0]='~';
for (int i=0; i<n; ++i)
s[i<<1|1]='#',s[(i<<1)+2]=s1[i];
s[n<<1|1]='#'; s[(n<<1)+2]='!';
n=(n<<1)+3;
p[0]=0;
ele id=0,mx=0;
for (int i=1; i<n; ++i){
if (i<=mx)
p[i]=min(p[id*2-i],mx-i);
else p[i]=0;
for (; s[i-p[i]-1]==s[i+p[i]+1]; ++p[i])
;
if (i+p[i]>mx){
id=i;
mx=i+p[i];
}
}
root=build(1,n);
ele ans=0;
for (int i=0; i<n; ++i){
ele j=query(root,1,n,i+1,n,i+p[i]);
ans=max(ans,j-i);
}
printf("%d\n",ans);
return 0;
}