回文子串的最大长度(二分+Hash)

在这里插入图片描述

题意:

题面意思

思路:

上一道兔子兔子的题目,我们知道判断两个字符串是否相等,可以使用字符串哈希,也就是将字符串算成P进制数值,然后区间和判断即可,那么这道题目我们需要一个正的字符串,还需要一个反的字符串,然后如果正字符串等于反的字符串之所以要这么做,因为我们是要回文串所以我们需要将回文拆解成为一个正字符串和一个反字符串,这样才好处理这道题目.
既然如此,我们可以算出一个前缀和,再算出一个后缀和,然后就可以知道,正字符串和一个反字符串.字符串的哈希值就是这个区间的哈希值和.
算完之后,我们当前就只需要枚举一个mid中间点,因为所有回文串都是有一个中间点(奇),或者中间区间(偶),然后二分分别寻找这个字符串长度即可,记住不是回文串,回文串的长度,是 字符串长度×2 + 1(奇) 或者是 字符串长度×2(偶数).
感觉马拉车更好,不记得了逃

AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
const int N = 1e6+10;
const int Mod = 131;
char s[N];
ull f1[N],f2[N],p[N];
int ans,t,l,r,mid;
ull Hash1(int i,int j) {//正字符串的哈希值
    return (f1[j]-f1[i-1]*p[j-i+1]);
}
ull Hash2(int i,int j) {//反字符串的哈希值
    return (f2[i]-f2[j+1]*p[j-i+1]);
}
void init() {
    p[0]=1;
    for(int i=1;i<=N-1;i++) p[i]=p[i-1]*131;
}
int main()
{
    init();
    while(++t) {
        ans=0;
        scanf("%s",s+1);
        int len=strlen(s+1);
        if(strcmp(s+1,"END")==0) return 0;
        f2[len+1]=0;
        for(int i=1;i<=len;i++) 
            f1[i]=f1[i-1]*Mod+(s[i]-'a'+1);//前缀和
        for(int i=len;i>=1;i--)
            f2[i]=f2[i+1]*Mod+(s[i]-'a'+1);//后缀和
        for(int i=1;i<=len;i++) {
            l=0,r=min(i-1,len-i);//二分枚举长度为奇数的字符串
            while(l<r) {
                mid=l+r+1>>1;
                if (Hash1(i-mid,i-1)==Hash2(i+1,i+mid))//如果这是一个回文串的话 
                    l=mid;
                else
                    r=mid-1;
            }
            ans=max(l<<1 | 1,ans);//算出最大长度
            l=0,r=min(i-1,len-i+1);//偶数字符串
            while (l<r) {
                mid=l+r+1>>1;
                if (Hash1(i-mid,i-1)==Hash2(i,i+mid-1))//check判断
                    l=mid;
                else
                    r=mid-1;
            }
            ans=max(l<<1,ans);//偶数字符串只需要*2
        }
        printf("Case %d: %d\n",t,ans);
    }
    return 0;
}


©️2020 CSDN 皮肤主题: 黑客帝国 设计师:上身试试 返回首页