Manacher算法及其扩展
假设字符串str长度为N,想返回最长回文子串的长度,时间复杂度O(N)
暴力解法,以每个字符为中心计算回文子串的长度,但是要给每个字符中间加一个特殊字符,例如
31211214 ---- #3#1#2#1#1#2#1#4# 这样计算出来的每个结果除2就是实际的回文子串长度
Manacher就是在这个暴力解法的基础上利用信息做加速
1、回文半径(从中心开始数一半)、回文直径(整个回文串的长度)
2、回文半径数组 pArr[] 记录每个位置得出的答案
3、回文最右边界 R
4、中心 C
i 在 R 外,没有优化
(1)i 在 R 内,那么一定存在一个i' 在以C为中心的左半
[abcdcstetscdcba] i' 在 L .. R 内 i 不需要再计算和i'的回文半径一样
L i' C i R
(2) i' 在回文区域在L..R 外 i 也不需要计算,回文半径是 i 到 R 的距离
(3) i' 的回文区域在L上,从R下一个开始往外扩
public static int manacher(String s) {
if (s == null || s.length() == 0) {
return 0;
}
char[] str = manacherString(s);
// 回文半径的大小
int[] pArr = new int[str.length];
int C = -1;
int R = -1;// 讲述中:R代表最右的扩成功的位置。中:最右的扩成功位置的,再下一个位置
int max = Integer.MIN_VALUE;
for (int i = 0; i != str.length; i++) {
// i位置扩出来的答案,i位置扩的区域,至少是多大。
pArr[i] = R > i ? Math.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;
}
max = Math.max(max, pArr[i]);
}
return max - 1;
}
public static char[] manacherString(String str) {
char[] charArr = str.toCharArray();
char[] res = new char[str.length() * 2 + 1];
int index = 0;
for (int i = 0; i != res.length; i++) {
res[i] = (i & 1) == 0 ? '#' : charArr[index++];
}
return res;
}
给定一个字符串,只能给后面添加字符,问最少添加几个能让整个字符串变成回文?
求出必须包含结尾字符的回文字符串的最左侧中心,添加没有被中心包住的字符,逆序加在后面
public static String shortestEnd(String str) {
if (str == null || str.length() == 0) {
return null;
}
char[] charArr = manacherString(str);
int[] pArr = new int[charArr.length];
int index = -1;
int pR = -1;
int maxContainsEnd = -1;
for (int i = 0; i != charArr.length; i++) {
pArr[i] = pR > i ? Math.min(pArr[2 * index - i], pR - i) : 1;
while (i + pArr[i] < charArr.length && i - pArr[i] > -1) {
if (charArr[i + pArr[i]] == charArr[i - pArr[i]])
pArr[i]++;
else {
break;
}
}
if (i + pArr[i] > pR) {
pR = i + pArr[i];
index = i;
}
if (pR == charArr.length) {
maxContainsEnd = pArr[i];
break;
}
}
char[] res = new char[str.length() - maxContainsEnd + 1];
for (int i = 0; i < res.length; i++) {
res[res.length - 1 - i] = charArr[i * 2 + 1];
}
// 后面要添什么
return String.valueOf(res);
}
public static char[] manacherString(String str) {
char[] charArr = str.toCharArray();
char[] res = new char[str.length() * 2 + 1];
int index = 0;
for (int i = 0; i != res.length; i++) {
res[i] = (i & 1) == 0 ? '#' : charArr[index++];
}
return res;
}