Mancher算法
解决问题为最长回文子串
经典解法,认定一个数字后往外扩,但会漏掉个数为偶数的回文数;经典解法的改进入下,后续除2便不会影响奇偶。所加入的字符不需要是原创中没出现的,因为比较不会是虚和实比较。实际复杂度O(N^2)
概念
- 回文直径7、回文半径4
- 回文半径数组
- 之前扩的所有位置中所到达的最右回文右边界R:右侧边界冲到最远的位置
- C:取得R时候中心点位置
策略
- 来到中心点时,不在右边界R内:没有优化,从中心暴力扩
- 中心点在右边界R内部:
2.1 i’的回文区域在LR的内部,则i的回文数和i’一样
2.2 i’的回文区域跑到LR的外部,内部也有,则i的回文半径为i-R这段
2.3i’的回文区域压线,则i的回文i-R不用验证,需要扩
综上,可以利用以下伪代码分析时间复杂度为O(N^2)。
代码
string manacherString(string str) {
string res = "#";
for (char ch : str) {
res += ch;
res += "#";
}
return res;
}
int maxLcpsLength(string s) {
if (s.length() == 0) {
return 0;
}
string str = manacherString(s);
int R = -1;// 为右边界的下一个
int C = -1;// 为最右边界的中心点
int maxm= INT32_MIN;
vector<int> parr(str.length());
for (int i = 0; i < str.length(); 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;
}
maxm = max(parr[i], maxm);
}
return maxm - 1;
}