题意:
简单粗暴,求一个字符串中最长回文串的长度
总结:
说明:【my,mx】 是以 id 为中心的最长回文串,j 是 i 关于 id 的对称点 (图是偷的)
简述manacher算法实现:
(1)字符串预处理:
将 'abbacd' 处理成 '#a#b#b#a#c#d#',原因如下
(2)len[i]的意义:
len[i] 表示以 s[i] 为中心最长的回文串的半径,因为枚举的是回文串的中心,所以偶数长的回文串也会有一个中心点 :'#'
(3)计算len[i]:
枚举每个点为回文串的中心点时,利用前面以id为中心且右边界mx最大的回文串,观察上图,i、j 处于回文串 id 的左右两边界内,所以 i 的左右两边和 j 的左右两边是一样的,所以在【my,mx】内,len[i] = len[j],超出这个范围的就暴力判断还能否加长
(4)重新预处理:
因为涉及到暴力判断,将 'abbacd' 处理成 '{#a#b#b#a#c#d#}',首尾不一样就行,这样处理后暴力时就不用判断边界
(5)细节:
mx尽量的大,暴力的就越少,最后的答案为最大的半径 - 1 --- 规律
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int MAXN = 1e6+55;
char s[MAXN],t[MAXN<<1];
int len[MAXN<<1];
int init()
{
int k = 0;
t[0] = '{';
int p = strlen(s);
for(int i = 0; i < p; ++i)
{
t[++k] = '#';
t[++k] = s[i];
}
t[++k] = '#';
t[++k] = '}';
return k;
}
int manacher()
{
int k = init();
int mx = 0,res = 0,id = 0;
for(int i = 1 ; i < k; ++i)
{
if(i < mx) len[i] = min(mx-i,len[2*id - i]);
else len[i] = 1;
while(t[i - len[i]] == t[i + len[i]]) len[i]++; //超出边界暴力判断
if(i + len[i] > mx) //使mx尽量大
{
id = i;
mx = i + len[i];
res = max(len[i],res);
}
}
return res-1;
}
int main()
{
int cases = 1;
while(~scanf("%s",s) && s[0] != 'E')
{
printf("Case %d: ",cases++);
printf("%d\n",manacher());
}
return 0;
}