详解见:
http://blog.youkuaiyun.com/xingyeyongheng/article/details/9310555解析:
首先预处理字符串,在字符串首尾和每两个字符之间插入特殊符号(例如’#’)使字符串变成奇数长度,为了进一步减少编码复杂度,还可以再字符串开始再加入另一个特殊字符,这样就不用特殊处理越界问题了。然后利用计算字符串的P[i]数组来记录以字符S[i]为中心的最长回文子串向左/右扩张的长度即回文半径。
eg:
S: # 1 # 2 # 2 # 1 # 2 # 3 # 2 # 1 #
P: 1 2 1 2 5 2 1 4 1 2 1 6 1 2 1 2 1
(p.s. 可以看出,P[i]-1正好是原字符串中以S[i]为中心形成回文串的总长度)
- 计算半径数组:
void manacher()//p[]为半径数组,str[]为处理过后字符串
{
int id, mx, i;
id = mx = 0;
for (i=1; i<=l; ++i)
{
if (mx > i) p[i] = min(p[2*id-i], mx-i+1);
else p[i] = 1;
while (str[i+p[i]]==str[i-p[i]]) ++p[i];
if (p[i]+i-1>mx)
{
id = i;
mx = i + p[i] - 1;
}
}
}
最后遍历一次半径数组,值最大的就是最长回文子串。
分析&题解:
直接用Manacher算法求最长回文子串,不知为何之前自己敲的一直TLE,感觉差不多,后来找了个AC代码,以后直接套模板吧 (= =)AC 代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int nn = 220050;
char str[nn], str0[nn];
int len, p[nn];
void manacher()
{
int id, mx, i;
id = mx = 0;
for (i=1; i<=len; ++i)
{
if (mx > i) p[i] = min(p[2*id-i], mx-i+1);
else p[i] = 1;
while (str[i+p[i]]==str[i-p[i]]) ++p[i];
if (p[i]+i-1>mx)
{
id = i;
mx = i + p[i] - 1;
}
}
}
int main(){
while (scanf("%s", str0) != EOF)
{
len = strlen(str0);
str[0] = '$'; str[1] = '#';
for (int i=0; i<len; ++i)
{
str[2*i+2] = str0[i];
str[2*i+3] = '#';//预处理数组
}
len = 2*len+1;
str[len+1] = '\0';
manacher();
int ans = 0;
for (int i=1; i<len; ++i)
if (ans < p[i]-1) ans = p[i]-1;
printf("%d\n", ans );
}
return 0;
}