1. 程式人生 > >【題解】Luogu P4324 [JSOI2016]扭動的迴文串

【題解】Luogu P4324 [JSOI2016]扭動的迴文串

原題傳送門

這題實際挺水的

先對兩個字串分別跑馬拉車

就能求出1、2類扭動迴文串最大的長度

考慮第三類的扭動迴文串\(S(i,j,k)\),一定可以表示為\(A(i,l)+A(l+1,j)+B(j,k)\)\(A(i,j)+B(j,l)+B(l+1,k)\),其中,第一段與第三段對稱(第一段正著Hash和第三段反著Hash相同,資料水(某八位質數都不卡),單模數hash就行),第二段是一個迴文子串,三段都可以是空串。

在A、B串上列舉扭動的迴文串的中心mid,不難發現,以其在原串上能擴展出的最長迴文子串為第二段進行擴充套件一定最優。對每個mid,以最長迴文子串作為第二段,在A、B串上二分第一、三段的長度,其結果一定最優

程式碼(極其醜,常數極大)

#include <bits/stdc++.h>
#define getchar nc
#define N 200005
#define P 19260817
using namespace std;
inline char nc(){
    static char buf[100000],*p1=buf,*p2=buf; 
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++; 
}
inline char gc(){
    char ch;
    while((ch=getchar())<'A'||ch>'Z');
    return ch;
}
inline int read()
{
    register int x=0,f=1;register char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return x*f;
}
inline void write(register int x)
{
    if(!x)putchar('0');if(x<0)x=-x,putchar('-');
    static int sta[20];register int tot=0;
    while(x)sta[tot++]=x%10,x/=10;
    while(tot)putchar(sta[--tot]+48);
}
inline int Min(register int a,register int b)
{
    return a<b?a:b;
}
inline int Max(register int a,register int b)
{
    return a>b?a:b;
}
int n,ans,p;
int a[N],b[N],sum[N],num[N],bin[N],f[N],g[N];
char ch; 
inline bool check(register int l1,register int r1,register int l2,register int r2){
    int x=0,y=0;
    x=(sum[r1]-1ll*sum[l1-1]*bin[r1-l1+1]%P)%P;
    y=(num[l2]-1ll*num[r2+1]*bin[r2-l2+1]%P)%P;
    x=(x+P)%P,y=(y+P)%P;
    return x==y?true:false;
}
inline int calc(register int j,register int k)
{
    int l=0,r=Min(j,n-k+1),res=0;
    while(l<=r)
    {
        int mid=l+r>>1;
        if(check(j-mid+1,j,k,k+mid-1))
            res=mid,l=mid+1;
        else
            r=mid-1;
    }
    return res;
}
int main()
{
    n=read(); 
    bin[0]=1;
    for(register int i=1;i<=n;++i)
        bin[i]=1ll*bin[i-1]*27%P;
    a[0]=b[0]=0;
    a[(n<<1)+2]=b[(n<<1)+2]=28;
    a[1]=b[1]=27;
    for(register int i=1;i<=n;++i)
        ch=gc(),a[i<<1]=ch-'A'+1,a[i<<1|1]=27;
    for(register int i=1;i<=n;++i)
        ch=gc(),b[i<<1]=ch-'A'+1,b[i<<1|1]=27;
    p=0;
    for(register int i=2;i<=n<<1;++i)
    {
        if(i<=p+f[p])
            f[i]=Min(f[(p<<1)-i],p+f[p]-i);
        while(a[i-f[i]-1]==a[i+f[i]+1])
            ++f[i];
        if(i+f[i]>p+f[p])
            p=i;
    }
    p=0;
    for(register int i=2;i<=n<<1;++i)
    {
        if(i<=p+g[p])
            g[i]=Min(g[(p<<1)-i],p+g[p]-i);
        while(b[i-g[i]-1]==b[i+g[i]+1])
            ++g[i];
        if(i+g[i]>p+g[p])
            p=i;
    }
    for(register int i=2;i<=n<<1;++i)
        ans=Max(ans,Max(f[i],g[i]));
    for(register int i=1;i<=n;++i)
        sum[i]=(1ll*sum[i-1]*27%P+a[i<<1])%P;
    for(register int i=n;i;--i)
        num[i]=(1ll*num[i+1]*27%P+b[i<<1])%P;
    int l=0,r=0;
    for(register int i=2;i<=n<<1;++i)
    {
        l=(i-f[i]+1)>>1,r=(i+f[i])>>1;
        ans=Max(ans,f[i]+(calc(l-1,r)<<1));
        l=(i-g[i]+1)>>1,r=(i+g[i])>>1;
        ans=Max(ans,g[i]+(calc(l,r+1)<<1));
    }
    write(ans);
    return 0;
 }