您的位置:首页 > 其它

最长回文子串:Manacher算法

2016-07-15 11:07 197 查看
详解见:

http://blog.csdn.net/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;
}
}
}


最后遍历一次半径数组,值最大的就是最长回文子串。

练习:

http://acm.hdu.edu.cn/showproblem.php?pid=3068

分析&题解:

直接用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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息