Description
有两个长度均为nnn的字符串AAA和BBB。可以从AAA中选一个可以为空的子串A[l1..r1]A[l_1..r_1]A[l1..r1],BBB中选一个可以为空的子串B[l2..r2]B[l_2..r_2]B[l2..r2],满足r1=l2r_1=l_2r1=l2,然后把它们拼起来(A[l1..r1]+B[l2..r2])(A[l_1..r_1]+B[l_2..r_2])(A[l1..r1]+B[l2..r2])。求用这样的方法能得到的最长回文串的长度。注意:求的不是本质不同的回文串个数哦!!!
Input
第一行一个数nnn
第二行表示字符串AAA
第三行表示字符串BBB
(1≤n≤105)(1\le n\le 10^5)(1≤n≤105)
Output
输出一行一个数表示答案
Sample Input
5
ZQZFC
NSZXL
Sample Output
3
Solution
一个显然的结论是,所选子串必然至少有一个是原串中的极大回文串(或者说答案必然可以写成这样的形式),不妨认为是AAA串的一个极大回文串拼上AAA串左端一个子串和BBB串一个子串构成,那么后两部分需要回文,将BBB串翻转后这两部分需要相同,首先对AAA串跑一遍ManacherManacherManacher,然后二分两端拼接的长度,把AAA和BBB的反串hashhashhash以快速判断两个子串是否相同
Code
#include<cstdio>
#include<algorithm>
using namespace std;
typedef unsigned long long ull;
const int maxn=200005;
char A[maxn],B[maxn],s[maxn];
int len[maxn];
void Manacher(char *s,int n)
{
len[0]=1;
int mx=0,id=0;
s[n]='#';
for(int i=1;i<n;i++)
{
len[i]=mx>i?min(len[2*id-i],mx-i):1;
while(i-len[i]>=0&&s[i+len[i]]==s[i-len[i]])len[i]++;
if(i+len[i]>mx)
{
mx=i+len[i];
id=i;
}
}
for(int i=0;i<n;i++)len[i]=2*len[i]-1;
}
ull base=17,b[maxn];
struct Hash
{
ull h[maxn];
void init(char *s,int n)
{
h[0]=s[0];
for(int i=1;i<n;i++)h[i]=h[i-1]*base+s[i];
}
ull query(int l,int r)
{
if(l==0)return h[r];
return h[r]-h[l-1]*b[r-l+1];
}
}ha,hb;
int n;
int Solve(char *A,char *B)
{
Manacher(A,n);
reverse(B,B+n);
ha.init(A,n),hb.init(B,n);
int ans=0;
for(int i=0;i<n;i++)
{
int l1=i-len[i]/2,r1=i+len[i]/2;
int l2=n+1-r1;
int l=0,r=min(l1,l2),mid,temp=0;
while(l<=r)
{
mid=(l+r)/2;
if(ha.query(l1-mid,l1-1)==hb.query(l2-mid,l2-1))l=mid+1,temp=mid;
else r=mid-1;
}
temp=2*temp+len[i];
if(i&1)temp=(temp+1)/4*2;
else temp=temp/4*2+1;
ans=max(ans,temp);
}
return ans;
}
int main()
{
scanf("%d",&n);
scanf("%s",s);
for(int i=n-1;i>=0;i--)A[2*i]=s[i];
scanf("%s",s);
for(int i=n-1;i>=0;i--)B[2*i]=s[i];
n=2*n-1;
b[0]=1;
for(int i=1;i<=n;i++)b[i]=b[i-1]*base;
int ans=Solve(A,B);
reverse(A,A+n);
ans=max(ans,Solve(B,A));
printf("%d\n",ans);
return 0;
}