洛谷 P4287 [SHOI2011]双倍回文题解

前言

用了一种很奇怪的方法来解,即二分判断回文,再进行某些奇怪的优化。因为这个方法很奇怪,所以希望如果有问题能够 hack 一下。

题解

我们发现,这题中要求的是字符串 \(SS'SS'\),其中 \(S'\)\(S\) 的反向,那么这个串长度必定为 \(4\) 的倍数。那么我们对于每个位置,寻找一组最大的 \(SS'\),然后找这前面是否有一个与他相同的 \(SS'\),如果有,那么这两组字符串组成一个双倍回文。但是问题在于对于某些情况,例如 bbbbbbbbbbb,在第 \(3\)\(4\) 个字符中间以及第 \(8\)\(9\) 个字符中间,都能取到长度为 \(3\) 的回文 bbb,但是他们共用了第 \(6\) 个字符,从而造成最大双倍回文子串长度为 \(3\) 的假象。为了避免这种假象的发生,同时保证答案长度的最大化,我们需要将共用的部分一分为二,各执一半。

仔细思考,我们可以发现,这一共用的部分只可能是一个字符集大小为 \(1\) 的字符串(语文不好不知道怎么表达)。因为我们知道,回文子串关于对称中心对称,即在该回文子串长度范围内与对称中心距离相同的两个字符相同,两个串共用的部分位于一个串 \(A\) 的末尾和另一个串 \(B\) 的开头,当我们把串 \(B\) 的开头映射到串 \(A\) 的开头(因为 \(A\) 串和 \(B\) 串相同)后,可以发现,只有符合字符集大小为 \(1\) ,才能符合上述性质。

因此,我们预处理时把每一段字符集大小为 \(1\) 的长度等信息做好标记,然后在计算完各位置为中心的最大回文子串长度后减去重复长度即可。

代码

#include<cstdio>
#include<map>
#include<algorithm>
typedef unsigned long long ull;
const int MAXN=500001+5;
std::map<ull,int>map;
char str[MAXN];int tail[MAXN],n,l,rs[MAXN],r,len[MAXN],bll[MAXN],now,cnt,mid;ull cc,hash1[MAXN],hash2[MAXN],pow[MAXN];
ull GetHash1(int l,int r)
{
    if(l<1||l>n||r<1||r>n) return ++cc;
    return hash1[r]-hash1[l-1]*pow[r-l+1];
}
ull GetHash2(int l,int r)
{
    if(l<1||l>n||r<1||r>n) return ++cc;
    return hash2[l]-hash2[r+1]*pow[r-l+1];
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%c",&str[i]);
        if(!(str[i]>='a'&&str[i]<='z')) i--;
    }
    for(int i=1;i<=n;i++)
    {
        if(str[i]!=str[i-1]) {tail[i-1]=1;now++;}
        rs[i]=now;
        bll[now]++;
    }
    for(int i=1;i<=n;i++)
        hash1[i]=hash1[i-1]*31+str[i]-'a'+1;
    for(int i=n;i>=1;i--)
        hash2[i]=hash2[i+1]*31+str[i]-'a'+1;
    pow[0]=1;
    for(int i=1;i<=n;i++)
        pow[i]=pow[i-1]*31;
    int ans=0,answer,aans=0,last=0;
    for(int i=1;i<=n;i++)
    {
        cnt=0;l=1;r=std::min(i-1,n-i+1);ans=0;answer=0;
        while(l<=r)
        {
            cnt++;
            if(cnt>30) break;
            mid=(l+r)>>1;
            if(GetHash1(i-mid,i-1)!=GetHash2(i,i+mid-1)) {r=mid-1;} else {l=mid+1;ans=std::max(ans,mid);}
        }
        if(tail[i+ans-1])
        ans-=std::min(ans,bll[rs[i+ans-1]])/2;
        len[i]=ans;
        int result=map[GetHash1(i-ans,i+ans-1)];
        if(GetHash1(i-ans,i+ans-1)==GetHash1(i+ans,i+ans*3-1))
        {
            aans=std::max(aans,ans*4);
        }

    }	
    printf("%d",aans);
    return 0;
}
posted @ 2019-01-21 09:03  酷暑一夏1  阅读(208)  评论(0编辑  收藏  举报