Mancher 最长回文字串
暴力方法 O(n^2)
1.缺陷版本
遍历每个字符
以每个字符为中间位置向左右两边扩一位。
不能发现偶数回文
2.改正版本
先再字符串每个字符之间插入 #
再逐个向两边阔
就不会错过偶数回文 得到的值 /2 就是每个位置(包括虚轴)的回文长度
插入的字符不一定必须是
#
,可以是任何字符,和原串内容的相同字符也可以
Mancher o(n)
mancher是以暴力的改正版本为基础,在暴力得到Arr[i]
的时候做优化。
在遍历过程中记录每一次扩的最右边界
当 i<R
内的时候,就能优化,否则继续暴力。
优化就是通过轴C
找到 i'的回文半径
判断三种情况
- i’的范围在区间内
[i] = [i']
直接优化 - i’的范围在区间外 回文半径至少是
R-i
- i’的范围压线 回文半径至少是
R-i || i'的回文半径
之后再while()
模板
// t为处理过的字符串,p为记录长度的数组
memset(p, 0, sizeof(p));
// mx为已判断回文串最右边位置,id为中间位置,mmax记录p数组中最大值
int mx = 0, id = 0, mmax = 0;
for (int i = 1; t[i]; i++) {
p[i] = mx > i ? min(p[2 * id - i], mx - i) : 1;
while (t[i + p[i]] == t[i - p[i]])
p[i]++;
if (i + p[i] > mx) {
mx = i + p[i];
id = i;
}
maxx = max(maxx,p[i]);
}
return mmax - 1;
code
#include <bits/stdc++.h>
using namespace std;
//string str = "#1#2#2#1#";
string getstrr(string s){ // s --> str
string str;
int len = s.length();
for(int i=0;i<len;i++){
str.push_back('#');
str.push_back(s[i]);
}
str.push_back('#');
return str;
}
int getmaxlen(string s){
string str = getstrr(s.c_str());
int parr[str.length()];
int C = -1;
int R = -1; //记录遍历过的回文最右的下一位
int maxx = -1;
for(int i=0;i<str.length();i++){
//i位置扩的区域 最小是多大 i' i的右半部分
parr[i] = R > i ? min(parr[2*C-i] , R-i):1;
while(i+parr[i] < str.length() && i-parr[i] > -1){
if(str[i+parr[i]]==str[i-parr[i]]){
parr[i]++;
}else{
break;
}
}
if(i+ parr[i] > R){
R = i + parr[i];
C = i;
}
maxx = max(maxx,parr[i]);
}
return maxx-1; //最长回文长度
}
int main()
{
string s = "1221";
cout<<getmaxlen(s.c_str())<<endl;
return 0;
}