-
Manacher算法原理
该算法原型就是求解字符串中的最长回文子串,字符串的长度可能是技术或者偶数,所以首先要对字符串进行填充,例如“abc”变为“#a#b#c#”。 -
算法中的三个要素
1)回文半径数组(以当前字符为中心所形成回文字符串的半径大小所组成的数组)
2)最右回文右边界(以当前字符为中心所形成回文字符串的最右边的字符的位置)
3)回文右边界的中心 -
如何求解回文半径数组
如果对每个字符都暴力的去扩充求回文半径,那么就不能实现O(n)的时间复杂度,所以求回文半径数组的时候需要分情况来讨论。
1)当前字符不再回文右边界内,直接暴力扩求其半径;
2)当前字符在回文右边界内部,再分三种情况:
1.假设当前最右回文右边界的回文中心是C,那么当前字符i关于C的对称字符 i‘ 的回文半径是已知的,当 i‘ 的回文字符串的左边界小于C的左边界,那么i的回文半径就等于 i’的回文半径。
2.假设当前最右回文右边界的回文中心是C,那么当前字符i关于C的对称字符 i‘ 的回文半径是已知的,当 i‘ 的回文字符串的左边界小于C的左边界,那么i的回文半径就等于i到C的右边界。
3.假设当前最右回文右边界的回文中心是C,那么当前字符i关于C的对称字符 i‘ 的回文半径是已知的,当 i‘ 的回文字符串的左边界等于C的左边界,那么i的回文半径就等于C的右边界继续往外扩,直到不再以i为中心形成回文,同时更新最右回文右边界。 -
代码实现
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 int maxLcpsLength(String str){
if(str==null || str.length()==0){
return 0;
}
char [] charArr = manacherString(str);
int [] pArr = new int [charArr.length];
int index = -1;//回文中心
int pR = -1;//回文右边界
int max = Integer_MIN_VALUE;
for(int i=0; i != charrArr.length; i++){
pArr[i] = i<pR? Math.min(pArr[2*index-i], pR-i) : 1;
while(i + pArr[i] < charArr.length&&i - pArr[i]){
if(charArr[i+pArr[i]] == charArr[i-pArr[i]]){
pArr[i]++;
}else{
break;
}
}
//更新回文右边界以及回文中心;
if(i + pArr[i] > pR){
pR = i + pArr;
index = i;
}
max = Math.max(max , pArr[i]);
}
return max - 1;
}