```java
import static jdk.nashorn.internal.objects.NativeMath.min;
public class Manacher {
public StringBuffer solution(String str){
char [] s=str.toCharArray();
char [] snew=new char[2*s.length+3];//给字符的两边添加特殊字符,把偶回文变成奇回文。
int j=0;
snew[j++]='$';//头一个字符与其他特殊字符区别开来,防止 while(snew[i+num[i]]==snew[i-num[i]])超出边界。
snew[j++]='#';
for(int i=0;i<s.length;i++){
snew[j++]=s[i];
snew[j++]='#';
}
snew[j]='¥';//最后一个字符与其他特殊字符区别开来,防止 while(snew[i+num[i]]==snew[i-num[i]])超出边界。
int []num=new int[snew.length];
int Max=1;//最靠右的回文半径的坐标
int flag=1;//拥有最靠右的回文半径字符的坐标
int maxlength=0;//记录整个字符数组最长的回文半径(用来获取最长回文字符串)
int p=0;//记录拥有整个字符数组最长的回文半径字符的坐标(用来获取最长回文字符串)
StringBuffer stringBuffer=new StringBuffer();
for (int i=1;i<snew.length-1;i++){//需要从i=1开始,否则i-num[i]会越界;
num[i]=Max>i?(int)min(num[2*flag-i],Max-i):1;//核心语句,根据i是否在Max里面计算i的回文右半径。
while(snew[i+num[i]]==snew[i-num[i]])//判断下一个字符是否构成回文
num[i]++;
if(Max<num[i]+i){
Max=num[i];
flag=i;
}
if(maxlength<num[i]){//记录整个字符数组最长的回文半径
maxlength=num[i];
p=i;//记录拥有整个字符数组最长的回文半径字符的坐标
}
}
for(int i=p-maxlength+1;i<p+maxlength-1;i++){//根据最长的回文半径及其坐标求出最长的回文字符串
if(snew[i]!='#'){
stringBuffer.append(snew[i]);
}
}
return stringBuffer;
}
public static void main(String[] args) {
Manacher manacher=new Manacher();
StringBuffer string=manacher.solution("dabagbaf");
System.out.println(string);
}
}
首先计算当前字符的最大回文半径r,分四种情况:
- i不在R里面;
- i对映的i’的r在左R中
- i对映的i’的r在超出左R
- i对映的i’的r在刚好在R的边界上
2,3两个情况一次判断即可得出结果,1,4两种情况还需判断后面的字符是否构成回文
下面代码是核心代码
num[i]=Max>i?(int)min(num[2*flag-i],Max-i):1;
while(snew[i+num[i]]==snew[i-num[i]])
num[i]++;
最后注意边界赋值
因为snew[i+num[i]]==snew[i-num[i]]使得遍历数组不能从0开始,否则i-num[i]会越界。同理也不能遍历字符数组的最后一个字符,否则i+num[i]会越界。