Manacher算法解决的问题:
字符串str中,最长回文子串的长度如何求解?
如何做到时间复杂度O(N)完成?
经典解法:时间复杂度O(N^2)
Manacher算法的解法与经典解法相似,但是有加速。
使用特殊字符扩充字符串,如"1221"扩充为"#1#2#2#1#",这样方便找字符个数为偶数的回文子串
特殊字符可以为任意字符,不会与字符串字符混淆,因为以任意字符为中心,比较左右两边字符时,总是实字符跟实字符比较,虚字符跟虚字符比较。
回文半径和回文直径:
"#a#1#2#1#b#",字符‘2’的回文半径是4,回文直径是7。
回文半径数组:
在从左往右遍历字符串时,把每个字符的回文半径记录下来
之前扩的所有位置中所到达的最右回文右边界R:初始值-1,向右只增不减
取得最右回文右边界时的中心点C:与R伴生
从左往右遍历时:
1)当前中心点不在最右回文右边界里:暴力扩
2)当前中心点在最右回文右边界内:
(1)i ' 的回文区域彻底在 (L ~ R) 里面 i 的回文半径 = i ' 的回文半径
(2)i ' 的回文区域在 [L , R] 外面 i 的回文半径 = R - i + 1
(3)i ' 的回文区域跟L压线 i 的回文半径 = R - i + 1,然后试着往外扩
时间复杂度:O(N)
string manacherString(string& s){
int n = s.size();
string str (2 * n + 1, '#');
for(int i = 0; i < n; i++){
str[2 * i + 1] = s[i];
}
return str;
}
int manacher(string& s){
if(s.empty()){
return 0;
}
string str = manacherString(s);
vector<int> arr (str.size(), -1);
int C = -1; // 中心
int R = -1; // 回文右边界
int res = INT_MIN;
for(int i = 0; i < str.size(); i++){
arr[i] = i < R ? min<int>(R - i + 1, arr[2 * C - i]) : 1;
while(i - arr[i] >=0 && i + arr[i] < str.size()){
if(str[i - arr[i]] == str[i + arr[i]]){
arr[i]++;
}
else{
break;
}
}
if( i + arr[i] - 1 > R){
R = i + arr[i] - 1;
C = i;
}
res = max<int>(res, arr[i]);
}
return res - 1;
}