poj3974 最长回文子串

题目描述

link

输入格式

多组数据,每次输入一个字符串,以"END"结尾

输出格式

对于每个字符串,输出它最长回文子串的长度

AC代码

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<string>
#include<cstring>
using namespace std;
int P=131,len;
char s[1000006];
unsigned long long p[1000006],hf[1000006],hb[1000006];
unsigned long long get1(int x,int y){/*
							s					1 2 3 4 5 6 7 8 9 10
							hf[r]				1 2 3 4 5
							hf[l-1]				1 2 3 
							hf[l-1]*p[r-l+1]	1 2 3 0 0
							4~5					0 0 0 4 5
									*/
	return (hf[y]-hf[x-1]*p[y-x+1]);
}
unsigned long long get2(int x,int y){
	return (hb[x]-hb[y+1]*p[y-x+1]);//注意:hb数组是相反的! 
}
int main()
{
	int id=0;
	p[0]=1;
	for(int i=1;i<=1000005;i++){
		p[i]=P*p[i-1];
	}
	while(scanf("%s",s+1)&&!(s[1]=='E'&&s[2]=='N'&&s[3]=='D')){
		id++;
		len=strlen(s+1);
		int ans=0;
		for(int i=1;i<=len;i++){
			hf[i]=hf[i-1]*P+s[i];
		}
		for(int i=len;i>=1;i--){
			hb[i]=hb[i+1]*P+s[i];
		}
		for(int i=1;i<=len;i++){
			int l=0,r=min(i-1,len-i);
			while(l<r){
				int mid=(l+r+1)>>1;
				if(get1(i-mid,i-1)==get2(i+1,i+mid))
					l=mid;
				else
					r=mid-1;
			}
			ans=max(ans,l*2+1);
			l=0,r=min(i-1,len-i+1);
			while(l<r){
				int mid=(l+r+1)>>1;
				if(get1(i-mid,i-1)==get2(i,i+mid-1))
					l=mid;
				else
					r=mid-1;
			}
			ans=max(ans,l*2);
		}
		printf("Case %d: %d\n",id,ans);
	}
	return 0;
}

思路整理

这题暴力思路很明显,我们只要枚举每个字符做回文中心,逐位向两侧扩展判断字符是否相同就OK。然而数据很大,O(n^2)的复杂度100%会TLE。

不过从暴力思路中,我们可以发现正解的影子。同样是向两侧扩展,我们没有必要逐位枚举。

这时我们可以先预处理字符串的两个哈希值(一个向前,一个向后),然后枚举每个字符做回文中心(注意奇回文和偶回文)。这时,我们可以二分查找回文串的长度,范围从0到回文中心与字符串两侧的距离,再判断回文串相同的部分的哈希值是否相等(这时两个哈希值就有用了)。

于是经过了一番瞎搞,时间复杂度成功被我们降到O(nlogn)。这样一来解题就没有困难了

然而在实际操作时,l到r区间Hash值的求法对我来说很难理解(本人菜请见谅),于是我做了一个这个:

						s					1 2 3 4 5 6 7 8 9 10 
                        
						hf[r]				1 2 3 4 5 
                        
						hf[l-1]				1 2 3  
                        
						hf[l-1]*p[r-l+1]	1 2 3 0 0 
                        
						4~5					0 0 0 4 5 

在这个简陋的表格中,每进一位(数位)相当于哈希值P,于是r的哈希值减去经过补位(也就是(r-l+1)个p)后的l的哈希值,就可以得出l到r区间的哈希值。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值