一.算法过程分析
由于回文分为偶回文(比如
bccb
b
c
c
b
)和奇回文(比如
bcacb
b
c
a
c
b
),而在处理奇偶问题上会比较繁琐,所以这里我们使用一个技巧,在字符间插入一个字符(前提这个字符未出现在串里)。举个例子:s="abbahopxpo"
,转换为s_new="$#a#b#b#a#h#o#p#x#p#o#"
(这里的字符 $ 只是为了防止越界,下面代码会有说明),如此,s 里起初有一个偶回文 abba
和一个奇回文 opxpo
,被转换为 #a#b#b#a#
和 #o#p#x#p#o#
,长度都转换成了奇数。
定义一个辅助数组 int p[]
, p[i]
表示以 ma[i]
为中心的最长回文的半径,例如:
i | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
ma[i] | $ | # | a | # | b | # | b | # | a | # | h | # | o | # | p | # | x | # | p | # |
p[i] | 1 | 2 | 1 | 4 | 5 | 2 | 1 | 2 | 1 | 2 | 1 | 2 | 1 | 2 | 1 | 6 | 1 | 2 | 1 |
可以看出,p[i]-1
正好是原字符串中最长回文串的长度。
二.定义 R 为以 ma[id]
为中心的最长回文最右边界,也就是 R = id + p[id]
。
j
j
与 关于
id
i
d
对称,根据回文的性质,p[i]
的值基于以下三种情况得出:
(1). $j$ 的回文串有一部分在 $id$ 的**之外**,如下图:
上图中,黑线为
id
i
d
的回文,
i
i
与 关于
id
i
d
对称,红线为
j
j
的回文。那么根据代码此时p[i] = R-i
,即紫线。那么p[i]
还可以更大么?答案是不可能!见下图:
假设右边新增的紫色部分是p[i]
可以增加的部分,那么根据回文的性质, 等于
d
d
,也就是说 的回文不仅仅是黑线,而是黑线 + 两条紫线,矛盾,所以假设不成立,故p[i] = R-i
,不可以再增加一分。
(2)j 回文串全部在 id i d 的内部,如下图:
此时p[i] = p[j]
,那么p[i]
还可以更大么?答案亦是不可能!见下图:
假设右边新增的红色部分是p[i]
可以增加的部分,那么根据回文的性质,
a
a
等于 ,也就是说
j
j
的回文应该再加上 和
b
b
,矛盾,所以假设不成立,故p[i] = p[j]
,也不可以再增加一分。
(3) 回文串左端正好与 id i d 的回文串左端重合,见下图:
此时p[i] = p[j]
或 p[i] = R - i
,并且p[i]
还可以继续增加,所以需要
while (ma[i - p[i]] == ma[i + p[i]])
p[i]++;
代码如下:
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn = 110010;
char s[maxn];
char Ma[maxn * 2];
int p[maxn * 2];
void Manacher(char s[], int len) {
int l = 0;
Ma[l++] = '$';
Ma[l++] = '#';
for(int i = 0; i < len; i++) {
Ma[l++] = s[i];
Ma[l++] = '#';
}
Ma[l] = 0;
int pos = 0, R = 0;
for(int i = 0; i < l; i++) {
if(i < R)p[i] = min(p[pos * 2 - i], R - i);
else p[i] = 1;
while(Ma[i + p[i]] == Ma[i - p[i]])p[i]++;
if(i + p[i] > R) R = i + p[i], pos = i;
}
}
int main() {
while(scanf("%s", s) != EOF) {
int Max = 0;
int len = strlen(s);
Manacher(s, len);
for(int i = 0; i < 2 * len + 2; i++) {
Max = max(Max, p[i] - 1);
}
printf("%d\n", Max);
}
return 0;
}