HDU 3068 最长回文

本文介绍Manacher算法,一种高效的寻找字符串中最长回文子串的方法。通过在原字符串中插入无关字符解决奇偶性问题,并利用P数组记录各字符为中心的回文半径,实现O(n)时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最长回文

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

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

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3068

题意:如题。

解题思路:今天学了一个好东西,叫做Manacher算法,用来查找最长回文,下面呢,我试着说一下它的思想,能不能说清楚是另外一件事啦,哈哈。

首先这个算法有很多好处:

1、它解决了字符串长度的奇偶带来的不便,奇偶性不影响它的计算。

2、它在时间复杂度上面比朴素算法优秀太多。

核心:

1、将输入的字符串S其尾每两个相邻的字符串中间插入一个无关字符,何为无关字符,就是在原字符串中没有出现的字符,此题中以#为例。例如:abab变为#a#b#a#b#。(解决了对称轴的奇偶性)

2、P[]数组,P[i]表示插入无关字符后的以S[i]为对称轴的回文字符串的半径。

其实只要得到了P数组,那么最后扫描一遍P数组找到最大的回文串长度输出就好,那么现在的关键就是如何高效地求解P数组。P数组的求解有点类似扩展KMP求extend数组。max_right为匹配到达的最右位置,pos为此次匹配的对称轴,当现在要求P[i]时,如果i<max_right,我们知道P[pos*2-max_rigtht...pos-1]=P[pos+1...max_right]。现在i<max_right,那么我们知道以P[i]=max(P[pos*2-i],max_right)。如果i>=max_right,那么P[i]=1,再根据P[i]的值开始向两边扩展匹配,记得更新max_right和pos,P[i]-1就是原字符串的回文长度(具体可以自己画图或者参考大佬关于该算法的详细介绍

AC代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXL = 110000 + 10; //字符串的最长长度 
char str[MAXL]; //字符数组 
char S[2*MAXL]; //插入无关字符后的数组 
int P[2*MAXL]; //记录每个字符作为回文对称轴的最长回文半径 
void init() //初始化S 
{
	memset(S,'\0',sizeof(S)); 
	S[0]='#'; //起始位置插入无关字符 
	int i,j; 
	for(i=0,j=1;str[i]!='\0';i++) //相邻字符之间插入 
	{
		S[j++]=str[i];
		S[j++]='#';
	}
	S[j]='\0';
}
void getP() //得到P数组 
{
	memset(P,0,sizeof(P));
	int pos=0; //匹配最右的对称轴 
	int max_right=0; //匹配的最右位置 
	for(int i=1;S[i]!='\0';i++) //获得每个字符作为对称轴的回文半径 
	{
		if(max_right>i) P[i]=min(P[2*pos-i],max_right-i); //i小于max_right 
		else P[i]=1;//i大于max_right 
		for(;i-P[i]>=0 && S[i-P[i]]==S[i+P[i]];P[i]++) ; //根据P[i]的值开始左右匹配 
		if(i+P[i]>max_right) //更新max_right和pos 
		{
			max_right=i+P[i];
			pos=i;
		}
	}
}
void solve()
{
	init(); //初始化函数 
	getP(); //获到P数组 
	int ans=0;
	for(int i=1;S[i]!='\0';i++)
	{
		ans=max(ans,P[i]-1); //最大的回文串长度 
	}
	printf("%d\n",ans); 
}
int main(void)
{
	while(scanf("%s",str)==1) //输入字符串 
	{
		solve(); //处理函数 
	}
	return 0;
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值