找到最长回文子串的问题
暴力解
从中间往两边扩
奇数回文,偶数回文不好解决
每个字符两边加## 11311 -> #1#1#3#1#1# ,各字符的最大回文长度/2
时间复杂度O(N^2)
manacher解 时间复杂度O(N)
基础概念
回文半径直径数组
最右回文右边界:所有回文半径中最靠右的位置
回文右边界的中心:
可能性1:i不在回文右边界内,暴力扩
在回文右边界内,找到右边界的中心C,并确定i的对称位置,
可能性2:
可能性3:
可能性4;
/**
* Created by huajianJin on 2019/10/17.
* 最长回文子串O(N)
*/
public class Manacher {
//字符两边加#,便于求解回文个数是奇数个的子串,所求回文长度/2
private static char[] getManacherString(String str) {
char res[] = new char[str.length() * 2 + 1]; // 长度是2倍加1,每个元素左边都放一个#,最后一个元素右边放一个#
char[] chs = str.toCharArray();
int index = 0;
for (int i = 0; i < res.length; i++) {
res[i] = (i & 1) == 0 ? '#' : chs[index++];
}
return res;
}
private static int maxLcpsLength(String str) {
// 初始化数据
char[] chs = getManacherString(str);
int centre = -1;
int rightBound = -1;
int[] cpsCnt = new int[chs.length]; // 记录每个位置的回文子串长度
int maxLength = 0, maxCentre = -1;
for (int pos = 0; pos < chs.length; pos++) {
// 根据已求得回文长度判断pos的最长回文长度
cpsCnt[pos] = rightBound > pos ? Math.min(rightBound - pos, cpsCnt[2 * centre - pos]) : 1;
// 继续扩展pos位置的回文串
while (pos + cpsCnt[pos] < chs.length && pos - cpsCnt[pos] > -1) {
if (chs[pos + cpsCnt[pos]] == chs[pos - cpsCnt[pos]]) {
cpsCnt[pos]++;
} else
break;
}
// 若pos的回文长度大于当前最大右边界,更新右边界
if (pos + cpsCnt[pos] > rightBound) {
rightBound = pos + cpsCnt[pos];
centre = pos; // 更新当前中心
}
// 若pos的回文长度大于之前求得最大回文长度,更新回文长度
if (cpsCnt[pos] > maxLength) {
maxLength = cpsCnt[pos];
maxCentre = pos;
}
}
maxLength--;
// String res = new String(chs).substring(maxCentre - maxLength, maxCentre + maxLength + 1).replace("#", "");
// System.out.println(res);
return maxLength;
}
public static void main(String[] args) {
String str = "abc1234321cab";
System.out.println(new String(getManacherString(str)));
int res = maxLcpsLength(str);
System.out.println(res);
}
}