题目地址
说明及示意图
1.在枚举点
i
的时候,因为i
在以mid
为中心的最长回文串中,所以点i
和点j
关于mid
对称,p[i] = p[j]
,因为(i + j) / 2 == mid
,所以j = 2 * mid - i
, 即p[i] = p[mid * 2 - i]
;
2.如果以点j
为中心的最长回文串的左边超出以mid
为中心的最长回文串的左边,则p[i] = mr - i
;
3.综上所述,p[i] = min(p[mid * 2 - i], mr - i)
.
代码
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 2e7 + 10;
int n;
char a[N], b[N]; // a是原串,b是初始化后的新串
int p[N]; //以b[i]为中心的最长回文半径
void init()
{
int k = 0;
b[k++] = '$', b[k++] = '#';
for (int i = 0; i < n; i++)
{
b[k++] = a[i];
b[k++] = '#';
}
b[k++] = '^';
n = k;
}
void manacher()
{
int mr = 0, mid; // mr(mid_right)是以mid为中心的最大回文串的右边界
for (int i = 1; i < n; i++)
{
if (i < mr) p[i] = min(p[mid * 2 - i], mr - i);
else p[i] = 1;
while (b[i - p[i]] == b[i + p[i]]) p[i]++; // 向两边扩展
if (i + p[i] > mr) // 以i为中心的最长回文串的右边界大于以mid为中心的右边界
{
mr = i + p[i]; // 更新最大回文串的右边界
mid = i; // 更新最大回文串的中心点
}
}
}
int main()
{
scanf("%s", a);
n = strlen(a);
init();
manacher();
int res = 0;
for (int i = 0; i < n; i++) res = max(res, p[i]);
printf("%d\n", res - 1);
return 0;
}