题意:
记字符串A。A反转过来是B。在给定的一个字符串里找到最长的形如ABAB的子串。这个子串称为双倍回文。
考虑用Manacher算法。(不会的自行google即可呃)
首先每个字符之间加一个分隔符#。然后跑一遍Manacher得到p数组。
拿字符串abbaabba举例子。如图。
然后我们从小到大开始枚举中心点i。
因为从i往右扩展的最远的地方是r=i+p[i]-1。那么我们只要在i~(i+r)/2中找到一点j使得j-p[j]+1<=i,也就是说s[i..j]是回文串。根据对称性,那么s[j..j+j-i]也是回文串。同理左半边也是回文串。那么就满足双倍回文的条件了。因为找的是最长的,所以j要尽可能大。
举个例子,假设枚举到i=9的时候。最大可以扩展到17。那么这个时候我们只要在9~13((9+17)/2=13)中找到一个最大的j,满足j-p[j]+1<=i。明显j=13。j-p[i]+1=13-5+1=9。
所以可以更新答案。ans=max(ans,(j-i)*4)。最后ans要除2。因为有一半的字符是#。
做的时候呢,记录每个点i扩展到的最左点l[i]=i-p[i]+1,和i一起排个序。枚举到一个点k的时候,把所有的l[i]<=k的i插入treap。然后查询treap中小于等于(k+k+p[k]-1)/2的最大值。更新答案。
接下来就好做了。但是还是有些小细节。导致我WA数次。。
第一,枚举的中心点如果是字母不是#的话,明显不可行。为什么呢。因为这个答案串长是4的倍数。那么对称轴是虚的,不可能在子母上。因此枚举的中心点必须是#,所以下标一定是奇数。
第二,同理,中心点i扩展到最远的r=i+p[i]-1,这个r也一定是奇数。很明显r如果是偶数,那一定可以扩展到r+1。因为对称的所以两边都多一个#没问题。否则答案串的字母比#多。除2就不对了。
另外的话,边界问题小心一点。可以从第5个开始扫描,扫描到倒数第5个。
贴代码~
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxn 1050005
int p[maxn],u[maxn]={0};
char s[maxn],str[maxn];
void manacher(int n){
int id,mx=0;
p[0]=1;
for(int i=1;i<n;i++){
p[i]=1;
if(mx>i) p[i]=min(p[id*2-i],mx-i);
while(str[i+p[i]]==str[i-p[i]]) p[i]++;
if(i+p[i]>mx) mx=i+p[i],id=i;
}
}
struct Treap{
int val[maxn];
int pri[maxn],ch[maxn][2],tot,rt,size;;
inline void rotate(int &x,int f){
int y=ch[x][!f];
ch[x][!f]=ch[y][f];
ch[y][f]=x;
x=y;
}
inline void ins(int &x,int v){
if(x==0){
val[tot]=v;
pri[tot]=rand();
ch[tot][0]=ch[tot][1]=0;
x=tot++;
}else{
int f=v<val[x];
ins(ch[x][!f],v);
if(pri[x]<pri[ch[x][!f]])rotate(x,f);
}
}
inline int pred(int x,int y,int k){
if(!x)return y?val[y]:-1;
else if(k<val[x])return pred(ch[x][0],y,k);
else return pred(ch[x][1],x,k);
}
inline void ins(int v){ size++;ins(rt,v); }
inline int getpre(int x){ return pred(rt,0,x); }
inline void init(){ tot=1; rt=0; size=0; }
}tr;
typedef pair<int,int> pii;
pii a[maxn];
int main()
{
int len;
scanf("%d",&len);
scanf("%s",s);
int t=0;
str[t++]='?';
str[t++]='#';
for(int i=0;s[i];i++){
str[t++]=s[i];
str[t++]='#';
}
manacher(t);
for(int i=1;i<t;i++) a[i]=make_pair(i-p[i]+1,i);
sort(a+1,a+t);
tr.init();
tr.ins(-1);
int cur=1,ans=0;
for(int i=5;i<=t-5;i++){
while(a[cur].first<=i && cur<t){
tr.ins(a[cur].second);
u[a[cur].second]=1;
cur++;
}
if((i&1)==0)continue;
int t=(i+i+p[i]-1)>>1;
int p=tr.getpre(t);
if(p==-1)continue;
if(p%2==0){
p--;
while(p>i+1 && !u[p])p-=2;
}
if(p<=i+1)continue;
if(p-i>1 && (p-i)*4>ans) ans=(p-i)*4;
}
printf("%d\n",ans>>1);
return 0;
}