内存限制:256 MiB 时间限制:1000 ms
题目描述
JYY 有两个长度均为 N 的字符串 A 和 B。
一个「扭动字符串」S(i,j,k)S(i,j,k)S(i,j,k) 由 A 中的第 i 个字符到第 j 个字符组成的子串与 B 中的第 j个字符到第 k 个字符组成的子串拼接而成。 比如,若 A=’XYZ’,B=’UVW’,则扭动字符串S(1,2,3)=S(1,2,3)=S(1,2,3)=’XYVW’。
JYY 定义一个「扭动的回文串」为如下情况中的一个:
1. A 中的一个回文串;
2.B 中的一个回文串;
3.或者某一个回文的扭动字符串S(i,j,k)S(i,j,k)S(i,j,k)
现在 JYY希望找出最长的扭动回文串。
输入格式
第一行包含一个正整数 N。
第二行包含一个长度为 N 的由大写字母组成的字符串 A。
第三行包含一个长度为 N 的由大写字母组成的字符串B。
输出格式
输出的第一行一个整数,表示最长的扭动回文串。
样例输入
5
ABCDE
BAECB
样例输出
5
样例解释
最佳方案中的扭动回文串如下所示(不在回文串中的字符用 . 表示):
.BC..
..ECB
数据范围与提示
对于所有的数据, 1≤N≤105 1 ≤ N ≤ 10 5 。
题解
在 A,B A , B 两串上的回文串不管用 manacher m a n a c h e r 还是回文自动机都很好处理,关键在于解决“扭动字符串”的情况。我们考虑讨论回文串上最特殊的点——回文串的中点属于哪一个串。记讨论的回文中心为点 k k ,当在 A A 串上时,”扭动字符串”的拐点一定在串上以 k k 为中心形成的最长回文串的右端点;同理,当在 B B 串上时,”扭动字符串”的拐点一定在串上以 k k 为中心形成的最长回文串的左端点。因此,我们只需枚举的位置,并用 manacher m a n a c h e r 算法算出左右端点(一般与回文串中点有关的题,多会用到 manacher m a n a c h e r ),最后用二分法枚举最长能延伸的长度(回文串长度满足二分性质),并用 hash h a s h 检验即可( O(1) O ( 1 ) 检验回文串的重要方法)。
代码
#include<stdio.h>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cmath>
typedef long long ll;
using namespace std;
const int maxn=2e5+5;
const ll mod=1e9+7;
const ll seed=131;
ll n,ans,mi[maxn],inv[maxn];
struct node
{
ll len,pos,maxx,rad[maxn];
ll val1[maxn],val2[maxn];
char c[maxn],temp[maxn];
void init()
{
maxx=len=pos=0,c[++len]='$',val1[0]=val2[0]=0;
for(ll i=1;i<=n;i++) c[++len]=temp[i],c[++len]='$';
}
void manacher()
{
for(ll i=1;i<=len;i++)
{
if(i>maxx) rad[i]=1;
else rad[i]=min(rad[pos*2-i],maxx-i+1);
while(i-rad[i]>0&&i+rad[i]<=len&&c[i-rad[i]]==c[i+rad[i]]) rad[i]++;
if(i+rad[i]-1>maxx) maxx=i+rad[i]-1,pos=i;
if(rad[i]>ans) ans=rad[i];
}
}
void Hash()
{
for(ll i=1;i<=len;i++)
{
val1[i]=(val1[i-1]+c[i]*mi[i])%mod;
val2[i]=(val2[i-1]+c[i]*inv[i])%mod;
}
}
}A,B;
ll ksm(ll x,ll y)
{
ll temp=1;
while(y)
{
if(y&1) temp=temp*x%mod;
y>>=1,x=x*x%mod;
}
return temp;
}
bool check1(ll x,ll y)
{
ll x1=x-A.rad[x],x2=x1-y+1;
ll num1=(A.val1[x1]-A.val1[x2-1]+mod)*inv[x2]%mod;
x1=x+A.rad[x]-2,x2=x1+y-1;
ll num2=(B.val2[x2]-B.val2[x1-1]+mod)*mi[x2]%mod;
return num1==num2;
}
bool check2(ll x,ll y)
{
ll x1=x+B.rad[x],x2=x1+y-1;
ll num1=(B.val2[x2]-B.val2[x1-1]+mod)*mi[x2]%mod;
x1=x-B.rad[x]+2,x2=x1-y+1;
ll num2=(A.val1[x1]-A.val1[x2-1]+mod)*inv[x2]%mod;
return num1==num2;
}
int main()
{
scanf("%lld%s%s",&n,A.temp+1,B.temp+1),A.init(),B.init();
A.manacher(),B.manacher();
mi[0]=inv[0]=1,mi[1]=seed,inv[1]=ksm(seed,mod-2);
for(ll i=2;i<=A.len;i++)
{
mi[i]=mi[i-1]*mi[1]%mod;
inv[i]=inv[i-1]*inv[1]%mod;
}
A.Hash(),B.Hash();
for(ll i=1;i<=A.len;i++)
{
ll l=0,r=min(i-A.rad[i],A.len-(i+A.rad[i])+1);
while(l<=r)
{
ll mid=(l+r)>>1;
if(check1(i,mid)) l=mid+1;
else r=mid-1;
}
ans=max(ans,A.rad[i]+r);
}
for(ll i=1;i<=B.len;i++)
{
ll l=0,r=min(i-B.rad[i],B.len-(i+B.rad[i])+1);
while(l<=r)
{
ll mid=(l+r)>>1;
if(check2(i,mid)) l=mid+1;
else r=mid-1;
}
ans=max(ans,B.rad[i]+r);
}
printf("%lld\n",ans-1);
return 0;
}