算法竞赛进阶指南0x14:Palindrome

原题链接

如果一个字符串正着读和倒着读是一样的,则称它是回文的。

给定一个长度为 N 的字符串 S,求他的最长回文子串的长度是多少。

输入格式

输入将包含最多 30个测试用例,每个测试用例占一行,以最多 1000000 个小写字符的形式给出。

输入以一个以字符串 END 开头的行表示输入终止。

输出格式

对于输入中的每个测试用例,输出测试用例编号和最大回文子串的长度(参考样例格式)。

每个输出占一行。

输入样例:

abcbabcbabcba
abacacbaaaab
END

输出样例:

Case 1: 13
Case 2: 6

 解法:哈希+manachar扩展

跟上题兔子兔子差不多,O(n)预处理前缀哈希值,每次O(1)计算任意区间哈希值;

兔子兔子题解

求回文字符串,意味着左边区间的前缀哈希值=右边区间的后缀哈希值;

书上解法是二分寻找半径长度,判断是否回文,时间复杂度O(n*logn);

y总讲解

我们这里做一个manacher的优化,每两个字符间填一个'#'(包括开头结尾),这样不会影响前缀哈希值和后缀哈希值的计算结果;

不知道 manacher的看这里        manacher详解

优化后就不用考虑回文长度是偶数还是奇数了,因为添加字符后回文子串的中心一定有一个字符(字母或者'#');

这样就不会出现添加前偶回文字符串中心什么都没有的情况;

然后经过manechar优化后的字符串,它的真实的回文长度=回文半径-1,回文长度-2*回文半径=1;

例如abcbc,添加前长度=5,回文串为bcbc,长度=4;

添加后为#a#b#c#b#c#,长度=11,回文串=#b#c#b#c#,长度=9,半径=5,

真实回文长度=半径-1 = 4(这也是用了manachar的思想);

manacher优化后我们直接判断当前中心+已经判断的长度是不是一个回文串,如果是回文串,我们再试着增加它的回文长度;

如果不是的话我们直接跳过,这样时间复杂度几乎为O(n);

#include<iostream>
#include<string.h>
using namespace std;
const int N=1e7+10,base=131;//base表示进制
using ULL=unsigned long long;
char s[N],str[N];//s表示填充前的字符串,str表示填充后的字符串;
ULL h1[N],h2[N],p[N];//h1表示哈希前缀和,h2表示后缀和,p表示进制次方;
int main()
{
    int T=0;
   
    while(scanf("%s",s+1),strcmp(s+1,"END")){
         int n=strlen(s+1);
         int len=0;//len表示填充后的长度
        for(int i=1;i<=n;i++){
            str[++len]='#';//填充'#'
            str[++len]=s[i];
        }
        str[++len]='#';//末尾填充
        str[len+1]='\0';
       
        p[0]=1;
        
        for(int i=1,j=len;i<=len,j>=1;++i,--j){//求前缀和后缀,进制次方
            h1[i]=h1[i-1]*base+str[i]-'a'+1;
            p[i]=p[i-1]*base;
            h2[j]=h2[j+1]*base+str[j]-'a'+1;
        }
        
        int res=0,r=0;//res表示最终结果,r表示填充后的回文串的半径-1,也就是真实长度
        for(int i=1;i<=len;++i){
            r=res;
            if(i+r>=len) break;//判断边界
            if(h1[i+r]-h1[i-1]*p[r+1]!=h2[i-r]-h2[i+1]*p[r+1]) continue;//最大的半径组成的是不是回文串
            while(str[i+r+1]==str[i-r-1]&&i+r+1<=len&&i-r-1>0) r++;//如果是判断能否扩展
            res=max(res,r);
        }
        cout<<"Case "<<++T<<": "<<res<<endl;
    }
    return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值