[BZOJ4755] JSOI2016 扭动的回文串

问题描述

JYY有两个长度均为N的字符串A和B。一个“扭动字符串S(i,j,k)由A中的第i个字符到第j个字符组成的子与B中的第j个字符到第k个字符组成的子串拼接而成。比如,若A=’XYZ’,B=’UVW’,则扭动字符串S(1,2,3)=’XYVW’。

JYY定义一个“扭动的回文串”为如下情况中的一个:

1.A中的一个回文串;

2.B中的一个回文串;

3.或者某一个回文的扭动字符串S(i,j,k)

现在JYY希望找出最长的扭动回文串。

输入格式

第一行包含一个正整数N。

第二行包含一个长度为N的由大写字母组成的字符串A。

第三行包含一个长度为N的由大写字母组成的字符串B。

1≤N≤10^5。

输出格式

输出的第一行一个整数,表示最长的扭动回文串。

扫描二维码关注公众号,回复: 9070286 查看本文章

样例输入

5
ABCDE
BAECB

样例输出

5

样例解释

最佳方案中的扭动回文串如下所示(不在回文串中的字符用.表示):
.BC..
..ECB

链接

BZOJ

解析

对于第一种和第二种,直接对两个字符串分别Manacher即可。下面重点讨论第三种情况。

仔细思考以后可以发现,一个扭动的回文串由三部分组成:最左边和最右边的两个部分分别属于A和B,并且拼在一起后仍然是回文串。中间的一段是在A中或B中的一段回文串。我们不妨把中间这一段作为突破口。枚举回文串中心,假设得到的回文串是\([L,R]\)。如果这个串在A中,我们就要找到最长的子串\(S\)满足\(S\)\(A[1...L-1]\)的后缀且\(S\)反过来之后是\(B[R...n]\)的前缀。这个可以用二分加哈希来解决。回文串在B中是类似的道理。

代码

#include <iostream>
#include <cstdio>
#define N 200002
#define int long long
using namespace std;
const int p=199999;
const int mod=1000000007;
char a[N],b[N],tmp[N];
int n,i,ha[N],hb[N],fa[N],fb[N],poww[N],mid,r;
int hasha(int l,int r)
{
    return (ha[r]-ha[l-1]*poww[r-l+1]%mod+mod)%mod;
}
int hashb(int l,int r)
{
    return (hb[l]-hb[r+1]*poww[r-l+1]%mod+mod)%mod;
}
int cal(int L,int R)
{
    if(L<1||R>n||a[L*2]!=b[R*2]) return 0;
    int l=1,r=min(L,n-R+1),mid,ans;
    while(l<=r){
        mid=(l+r)/2;
        if(hasha(L-mid+1,L)==hashb(R,R+mid-1)) ans=mid,l=mid+1;
        else r=mid-1;
    }
    return ans;
}
signed main()
{
    scanf("%lld%s%s",&n,a+1,b+1);
    for(i=1;i<=n;i++) ha[i]=(ha[i-1]*p+a[i]-'0')%mod;
    for(i=n;i>=1;i--) hb[i]=(hb[i+1]*p+b[i]-'0')%mod;
    poww[0]=1;
    for(i=1;i<=n;i++) poww[i]=poww[i-1]*p%mod;
    for(i=1;i<=n;i++) tmp[i]=a[i];
    for(i=1;i<=n;i++) a[i*2-1]='#',a[i*2]=tmp[i];
    a[2*n+1]='#';a[0]='<';a[2*n+2]='>';
    for(i=1;i<=n;i++) tmp[i]=b[i];
    for(i=1;i<=n;i++) b[i*2-1]='#',b[i*2]=tmp[i];
    b[2*n+1]='#';b[0]='<';b[2*n+2]='>';
    n=2*n+1;
    for(i=1;i<=n;i++){
        if(i<r) fa[i]=min(fa[2*mid-i],fa[mid]-i+mid);
        while(a[i+fa[i]]==a[i-fa[i]]) fa[i]++;
        if(i+fa[i]>r) r=i+fa[i],mid=i;
    }
    mid=r=0;
    for(i=1;i<=n;i++){
        if(i<r) fb[i]=min(fb[2*mid-i],fb[mid]-i+mid);
        while(b[i+fb[i]]==b[i-fb[i]]) fb[i]++;
        if(i+fb[i]>r) r=i+fb[i],mid=i;
    }
    int ans=0;
    for(i=1;i<=n;i++) ans=max(ans,fa[i]-1);
    for(i=1;i<=n;i++) ans=max(ans,fb[i]-1);
    for(i=1;i<=n;i++){
        int l=(i-fa[i])/2+1,r=(i+fa[i])/2-1;
        ans=max(ans,fa[i]-1+cal(l-1,r)*2);
        l=(i-fb[i])/2+1,r=(i+fb[i])/2-1;
        ans=max(ans,fb[i]-1+cal(l,r+1)*2);
    }
    printf("%lld\n",ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/LSlzf/p/12293426.html