1 题意
求一个字符串的最长回文子串。
链接:link。
2 思路
2.1 Manacher
下面先介绍
M
a
n
a
c
h
e
r
Manacher
Manacher算法。
假设
s
t
r
str
str为待处理字符串,
l
e
n
[
i
]
len[ i ]
len[i]数组存放以该
s
t
r
[
i
]
str[ i ]
str[i]字符为中心的最长回文半径,
m
i
d
mid
mid为当前最长回文子串的中点,
m
x
mx
mx为当前最长回文子串的右边界.。
在开始算法之前,先在字符串中首部填充$,字符之间#。比如,字符串abaaba填充之后就变成了$#a#b#a#a#b#a#($与#未在字符串中出现)。
那么对当前位置
i
i
i有如下伪代码:(此时已经知道前
i
−
1
i - 1
i−1个字符的最长回文半径)
- 若 i < m x i < mx i<mx,则 l e n [ i ] = m i n ( l e n [ 2 × m i d − i ] , m x − i ) len[ i ] = min ( len[ 2 \times mid - i ] , mx - i ) len[i]=min(len[2×mid−i],mx−i)。
- 否则 l e n [ i ] = 1 len[ i ] = 1 len[i]=1。
- 对 i i i位置上的字符向两边进行匹配,更新 l e n [ i ] len[ i ] len[i]的值,更新结束后更新 m i d mid mid和 m x mx mx的值。
命题:如果
i
<
m
x
i < mx
i<mx,那么
i
i
i为中心的最长回文半径最少为
m
i
n
(
l
e
n
[
2
×
m
i
d
−
i
]
,
m
x
−
i
)
min ( len[ 2 \times mid - i ] , mx - i )
min(len[2×mid−i],mx−i)。
证明:
首先
i
<
m
x
i < mx
i<mx说明
i
i
i坐落于以
m
i
d
mid
mid为中心的回文子串中。在该回文子串范围内,左右两边是完全对称的,所以
i
i
i的最长回文半径可参考其对称点
2
×
m
i
d
−
i
2 \times mid - i
2×mid−i的最长回文半径。
已知其对称点
2
×
m
i
d
−
i
2 \times mid - i
2×mid−i最长回文半径为
m
i
n
(
l
e
n
[
2
×
m
i
d
−
i
]
,
m
x
−
i
)
min ( len[ 2 \times mid - i ] , mx - i )
min(len[2×mid−i],mx−i),所以
i
i
i的最长回文半径至少为
m
i
n
(
l
e
n
[
2
×
m
i
d
−
i
]
,
m
x
−
i
)
min ( len[ 2 \times mid - i ] , mx - i )
min(len[2×mid−i],mx−i),超出
m
x
mx
mx的部分暴力匹配即可。
$#a#b#a#a#b#a#中以#为中心的最长回文子串去掉#后对应原字符串的最长偶回文串,以原字符为中心的最长回文子串去掉#后对应原字符串的最长奇回文串。而去掉#后的最长回文串的长度正好等于回文半径减 1 1 1,这样问题转化为了求$#a#b#a#a#b#a#的最长回文半径。
2.1.1 时间复杂度分析
由于每个字符只会被匹配一次,所以时间复杂度为 O ( n ) \mathcal{O}(n) O(n)。
2.1.2 实现
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=110010;
char s[MAXN],Ma[MAXN*2];
int Mp[MAXN*2];
int 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 res=0;
for(int mx=0,id=0,i=0;i<l;i++){
Mp[i]=mx>i?min(Mp[2*id-i],mx-i):1;
while(Ma[i+Mp[i]]==Ma[i-Mp[i]]) Mp[i]++;
if(i+Mp[i]>mx) mx=i+Mp[i],id=i;
res=max(res,Mp[i]-1);
}
return res;
}
int main(){
while(~scanf("%s",s)){
printf("%d\n",Manacher(s,strlen(s)));
}
return 0;
}