HDU3068 最长回文(Manacher算法)

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


这道题的模板来自kuangbin大神,我加上了一点自己的图文解释。

判断回文串最麻烦的一点就是要分奇数和偶数考虑,而Manacher算法就是用一种巧妙的方法,将奇数和偶数放在一起考虑。

具体做法就是在原字符串每两个字符中插入分隔符,首尾也要加上分隔符,防止越界。用Ma[i]记录预处理后的字符串。

用一个数组Mp[i]记录以字符Ma[i]为中心的最长回文字符串的长度。

如下图所示:

如何计算Mp[i]呢?

首先我们先定义几个数。

int mx;//当前已经已经回文串最右字符的最大值

int id;//回文串最右字符的最大值的中心位置

分两种情况考虑。

第一种情况:i<mx

首先找到i关于id的对称位置j(id*2-i),然后还要分两种情况考虑。

1、如果以j为中心的最长回文串在以id为中心的最长回文串的里面,以i为中心的最长回文串就等于以j为中心的最长回文串。

Mp[i]=Mp[j];


2、如果以j为中心的最长回文串超过了以id为中心的最长回文串,那么一开始以i为中心的只能保证黄色部分一定是回文串,最右超过mx部分的只能重新开始匹配。

Mp[i]=mx-i;

我曾经有一瞬间是迷惑不解的,觉得应该是(mx-1)*2,但是我后来想起来处理后的字符串里有“#”啊,所以应该是mx-1。那么重新开始匹配的时候也是匹配了一个字符,就++,而不是+2。


另外,如果新计算的回文串右端点大于mx,就要更新mx和id哦!

第二种情况:

i>=mx

那就老老实实的一个一个重新匹配吧。

以下是完整的代码:

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
const int MAXN=110010;
using namespace std;
char s[MAXN];
char Ma[MAXN*2];
int Mp[MAXN*2];
void Manacher(char s[],int len)
{
	int l=0;
	Ma[l++]='$';
	Ma[l++]='#';
	for(int i=0;i<len;i++)
	{
		Ma[l++]=s[i];
		Ma[l++]='#';
	}
	Ma[l]=0;
	int mx=0,id=0;
	for(int i=0;i<l;i++)
	{
		Mp[i]=mx>i?min(Mp[2*id-i],mx-i):1;
		while(Ma[i+Mp[i]]==Ma[i-Mp[i]])
			Mp[i]++;
		if(i+Mp[i]>mx)
		{
			mx=i+Mp[i];
			id=i;
		}
	}
}
int main(void)
{
	while(scanf("%s",s)==1)
	{
		int ans=0;
		int len=strlen(s);
		Manacher(s,len);
		for(int i=0;i<2*len+2;i++)
			ans=max(ans,Mp[i]-1);
		printf("%d\n",ans);
	}
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值