HDU3068 最长回文

HDU3068 最长回文

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 16036    Accepted Submission(s): 5874


Problem Description
给出一个只由小写英文字符a,b,c...y,z组成的字符串S,求S中最长回文串的长度.
回文就是正反读都是一样的字符串,如aba, abba等
 

Input
输入有多组case,不超过120组,每组输入为一行小写英文字符a,b,c...y,z组成的字符串S
两组case之间由空行隔开(该空行不用处理)
字符串长度len <= 110000
 

Output
每一行一个整数x,对应一组case,表示该组case的字符串中所包含的最长回文长度.
 

Sample Input
  
  
aaaa abab
 

Sample Output
  
  
4 3
 

Source

Analisys

         这道题可以用一种叫做manacher 马拉车 的算法来做。

manacher算法

        一种求字符串中最长回文串的O(N)的算法。
        首先回文串有奇偶两种,即长度为奇数、偶数的回文串,为了方便地解决这个问题,我们就在原创中每两个字符中间加一个0(也可以是别的字符,只要一样就行,头尾不加),这样就都变成了奇串。但是实际操作中,我们并不用真的在数组里加0,只需要利用下标实现。
我们先假想我们已经在原串中加好了0,所有的字符下标都变成了原来的两倍,那么易知下标为偶数的字符对应原串中的字符,下标为奇数的字符为0
        我们令b[i]表示串中以第i个字符为对称中心的串的一半长度(奇串包含中心),注意这个长度是在原串中的长度,现在从头到尾开始枚举i,令rightest pointer(以下简记为rp)表示前面所有回文串中能达到的最右的位置(这个是在真实串中的位置),j表示这个串的对称中心。那么以i为中心的一个回文串由j位置对应过去一定也是一个回文串,因为你已经知道了那个对称串的b值,所以当前b值至少是b[2j-i](由等差中项的性质可知x+i=2j,所以x=2j-i),这个是可以证明的,画画图就很好理解了,然后你可以再暴力扩展当前串,扩展一旦发生,就可以令j=i,同时rp++
        这个算法用纯文字真的不好讲啊。。。(吐槽别在意)
        这个算法唯一混乱的地方就在于下标,注意只有i、j是指向加过0的串中的位置,其他变量都是针对原串的,因为我们加入0的初衷就是为了能处理奇偶串,并没有实际意义,所以只有i、j这两个循环变量是不一样的,其它的都记录的是正常值。还有你枚举的i是对称中心i,设两个变量lmid,rmid,lmid=i/2,rmid=(i+1)/2,分别表示对称中心(原串)的左右两个中心,当这个串是奇串时lmid==rmid,否则差1,在你扩展串时计算左右边界会很方便,( [lmid-b[i]+1,rmid+b[i]-1]就是这个回文串的范围 )。
        算法伪代码:
        b[0]=1;
        for(i=j=rp=0;i<=right;i++)
        begin
                lmid=i div 2, rmid=(i+1) div 2;
                b[i]=min( rp-i+1 , b[2*j-1] );
                for(;(lmid-b[i]>0) and (rmid+b[i]<=right) and (a[lmid-b[i]==a[lmid+b[i]]);j=i,rp++,b[i]++ );
        end
        PS:这个算法和“马拉车”真的没什么关系,只是谐音而已

Code

//hdu3068 最长回文 manacher 
#include <cstdio>
#include <cstring>
#include <algorithm>
#define maxlen 300000
using namespace std;
char s[maxlen];
int right, b[maxlen];
bool init()
{
	if(scanf("%s",s)==EOF)return false;
	right=strlen(s)-1;
	return true;
}
int manacher()
{
	int i, j, rp, lmid, rmid, ans;
	b[0]=1;
	j=0;
	rp=0;
	ans=1;
	for(i=1;i<=(right<<1);i++)
	{
		lmid=i>>1;rmid=(i+1)>>1;
		b[i]=min(rp-rmid+1,b[(j<<1)-i]);
		for(;lmid-b[i]>=0 && rmid+b[i]<=right
			&& s[lmid-b[i]]==s[rmid+b[i]];
			b[i]++,rp++,j=i);
		ans=max(ans,(b[i]<<1)-!(j&1));
	}
	printf("%d\n",ans);
}
int main()
{
	while(init())manacher();
	return 0;
}


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值