最长回文
Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 23719 Accepted Submission(s): 8673
回文就是正反读都是一样的字符串,如aba, abba等
两组case之间由空行隔开(该空行不用处理)
字符串长度len <= 110000
aaaa abab
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;
}