hihoCoder #1032 : 最长回文子串
这就是最典型的manacher算法题,听起来高大上,而且也很难说清楚,但是实际上并不难,就相当于数学的推理,推出一个公式即可直接解出。
题目如下:
时间限制:1000ms 单点时限:1000ms 内存限制:64MB 描述
小Hi和小Ho是一对好朋友,出生在信息化社会的他们对编程产生了莫大的兴趣,他们约定好互相帮助,在编程的学习道路上一同前进。这一天,他们遇到了一连串的字符串,于是小Hi就向小Ho提出了那个经典的问题:“小Ho,你能不能分别在这些字符串中找到它们每一个的最长回文子串呢?”
小Ho奇怪的问道:“什么叫做最长回文子串呢?”
小Hi回答道:“一个字符串中连续的一段就是这个字符串的子串,而回文串指的是12421这种从前往后读和从后往前读一模一样的字符串,所以最长回文子串的意思就是这个字符串中最长的身为回文串的子串啦~”
小Ho道:“原来如此!那么我该怎么得到这些字符串呢?我又应该怎么告诉你我所计算出的最长回文子串呢?
小Hi笑着说道:“这个很容易啦,你只需要写一个程序,先从标准输入读取一个整数N(N<=30),代表我给你的字符串的个数,然后接下来的就是我要给你的那N个字符串(字符串长度<=10^6)啦。而你要告诉我你的答案的话,只要将你计算出的最长回文子串的长度按照我给你的顺序依次输出到标准输出就可以了!你看这就是一个例子。”
样例输入
3
abababa
aaaabaa
acacdas
样例输出
7
5
3
原题链接:https://hihocoder.com/problemset/problem/1032?sid=1155817
manacher的详细算法思路可以见:http://blog.csdn.net/xingyeyongheng/article/details/9310555
思路他已经讲的很详细就不重复复述了,就讲一下自己遇到的坑
在第三种情况下,i-k回文串的左边与i回文串左边重合,那么就让p[i+k]=p[i-k]
这是没问题的,但是,如果插入了特殊字符之后,那就不能这么写了。
我一开始写的是
if(p[id]+id == i){
p[i] = p[2*id -i];
}
然后就wa了,一直觉得自己没错,对比一下AC代码,发现我只多了这一句,经过推敲发现,这一句确实是多余的,如果一定要加上,那么也该是如下,因为两端都加上了特殊字符。
if(p[id]+id-2 == i){
p[i] = p[2*id -i];
}
下面放上AC代码:
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<math.h>
using namespace std;
const int MAX = 1000005;
char arr[MAX*2];
int p[MAX*2];
int main(){
int num;
cin>>num;
while(num--){
scanf("%s",arr);
int len = strlen(arr);
for(int i=len;i>=0;i--){
arr[i+i+2] = arr[i];
arr[i+i+1] = '#';
}
//arr[len*2+2]='@';
arr[0] = '$';
//长度是从1-2*i+1,设置第一个和最后一个不同,是让下面的hile循环正常运行不越界
int id=0;
int maxlen=0;
for(int i=2;i<len*2+1;i++){
if(p[id]+id > i){
p[i] = min(p[2*id-i],p[id]-i+id);
}else if(p[id]+id-2 == i){
p[i] = p[2*id -i];
}else{
p[i]=1;
}
while(arr[i-p[i]] == arr[i+p[i]]){
p[i]++;
}
if(p[i]+i > p[id]+id){
id = i;
}
if(p[i] > maxlen){
maxlen = p[i];
}
}
cout<<maxlen-1<<endl;
}
return 0;
}