Manacher算法
(统计一个字符串中最大可组成的回文数)
1:原理
形如 abba,abcba 的字符串中,采用暴力匹配法,遍历每个元素,对该元素作为中心元素,向两侧延申比较(但该做法只适用于奇数串,偶数串无法满足)
1:初始化 :由于 奇+偶 = 偶+奇 = 奇数,可以通过添加无关元素法将串变成奇数串。
abba : #a#b#b#a#, abcba : #a#b#c#b#a# -->都转换为奇数串
char s[maxn],s1[maxn*2+10];
int p[maxn*2+10]; //注意p是s1串的回文项
int init(){
int j = 0;
for(int i=0;i<n;i++){
s1[j++] = '#';
s1[j++] = s[i];
}
s1[j++] = '#';
return j;
}
2:匹配过程的分类讨论:
###### 首先:c :回文中心 r:回文右端点
c 为已经确定的回文中心,L->c == R->c,遍历 i
(1)当且仅当 i <r 并且 i’ 的回文半径不超过L 时,有 p[i] == p[i’],而 i’ 实际 = C*2-i;
(2) 当且仅当 i<r 并且 i‘ 的回文半径超过 L时, p[i] = R-i;
###### 证明:当 p[ i' ] > i'-L时,x == y, 又由C的回文半径得 y == k,但是由于C的回文半径最大是到R,所以 x != z,因此 :z != k ,p[i]的回文半径只能到R,证毕。
(3)当且仅当 i<r 并且p[i’]的半径刚好到L:p[i]的半径有可能超出R,需按暴力法继续匹配:
(4)当 i>=r 的时候,即不在已知回文范围内,p[i] = 1;
3:关于maxn-1的理解:
maxn 确实是p[i]中的最大值,但是 s1[i] 是 s[i] 长度的两倍多,当匹配回文串相等的时候,会发现回文半径最左端与最右端一定 是 ‘#‘,所以减一是正解。
2:源代码
void Manacher(){
int n = init();
int c = -1,r = -1,maxn = -1;
for(int i=0;i<n;i++){
if(i>=r)
p[i] = 1;
else
p[i] = min(p[c*2-i], r-i); //情况 1,2
while(p[i]+i < n && i-p[i]>=0){ //情况 3
if(s1[p[i]+i] == s1[i-p[i]])
p[i]++;
else break;
}
if(p[i]+i > r){ //更新
r = p[i]+i;
c = i;
}
maxn = max(maxn, p[i]);
}
maxn = maxn-1; //重点,最后一定要减一
cout<<maxn<<endl;
}
3:经典例题
hdu 3068