Manacher算法主要用途是计算字符串中的最长回文子串的长度,借助了一个辅助数组p。
算法步骤
- 首先向原字符串中插入原字符串里不存在的字符,使字符串长度变为奇数。
- 接下来引入两个概念:
①maxx:表示字符串中当前已经计算过的最右字符的下标
②id:表示当前已经计算过的最右边界回文串的回文中心 - 若当前遍历到字符串的第i位,则有两种情况:
①i < maxx
: 找到当前位置在最右边界回文串中的对称位置,由于该位置已经计算过了,所以用它的回文半径与当前位置距右边界的距离进行比较。如果回文半径较小,则p[i]与对称位置相等;反之,则对距离外的部分进行暴力判断最终得出p[i]。
②i > maxx
:直接暴力判断
Java实现代码
public String longestPalindrome(String s) {
// manacher算法
// 首先给字符串增加从来没有出现过的字符(这里为#号)
StringBuilder ss = new StringBuilder();
ss.append('#');
for (int i = 0; i < s.length(); i++) {
ss.append(s.charAt(i)).append('#');
}
// p[i]表示以i为中心的最长回文子串的回文半径
int[] p = new int[ss.length()];
// maxx表示当前已经计算过的最右边界坐标,id表示当前已经计算过的最右边界回文串的回文中心
int maxx = -1, id = 0;
// 开始计算p数组
for (int i = 0; i < ss.length(); i++) {
// r表示回文子串的最小半径
int r = 1;
// 如果当前坐标在计算过的范围内
if(i <= maxx) {
// 取当前点距最右边界的距离和当前点的对称点的回文半径的最小值
r = Math.min(p[id] - i + id, p[2 * id - i]);
}
// 暴力判断更后面的地方
while ((i - r) >= 0 && (i + r) < ss.length() && ss.charAt(i - r) == ss.charAt(i + r)) {
r++;
}
// 更新回文边界
if(i + r - 1 > maxx) {
maxx = i + r - 1;
id = i;
}
// 记录当前回文半径
p[i] = r;
}
int maxr = 0, pos = 0;
for (int i = 0; i < p.length; i++) {
if(p[i] > maxr) {
maxr = p[i];
pos = i;
}
}
// maxr-1为最长回文子串长度
//下面是计算起点
pos = (pos - maxr + 1) / 2;
return s.substring(pos, pos + maxr - 1);
}
参考资料
1.b站视频马拉车(Manacher)回文串算法
2.UESTCACM 每周算法讲堂 manacher算法
3.Manacher算法及其Java实现