1)基础
计算字符串的最长回文字串最简单的算法就是枚举该字符串的每一个子串,并且判断这个子串是否为回文串,这个算法的时间复杂度为O(n3)的,而稍微优化的一个算法是枚举回文串的中点(动态规划),这里要分为两种情况,一种是回文串长度是奇数的情况,另一种是回文串长度是偶数的情况,枚举中点再判断是否是回文串,这样能把算法的时间复杂度降为O(n2),但数据大的话,依然让人无法满意,这时Manacher(谐音马拉车)算法横空出世,在线性时间复杂度内求出一个字符串的最长回文字串,达到了理论上的下界。但同时,此算法的应用也十分狭窄,只能解决此类问题。
2)基本思路
其实用动态规划来解决最长回文子串比暴力法好就好在利用了一些已经算过的,减少了重复计算。而马拉车算法就是进一步得再利用已经算法的部分,使得重复计算更少。
1)
因为字符串有可能是奇数个或偶数个,所以我们要统一一下以减少计算。
3)代码实现
package manacher;
public class PlalindromeString{
//预处理字符串,在俩个字符之间加上#
private String preHandleString(String s){
StringBuffer sb = new StringBuffer();
int len = s.length();
sb.append('#');
for(int i=0;i<len;i++){
sb.append(s.charAt(i));
sb.append('#');
}
return sb.toString();
}
}
//寻找最长回文子串
public String findLongestPlalindromeString(String s){
//先预处理字符串
String str = preHandleString(s);
//处理后的字符串长度
int len =str.length();
//右边界
int rightSide=0;
//右边界对应的回文串中心
int rightSideCenter=0;
//保存以每个字符为中心的回文长度一半(向下取整)
int[] halfLenArr = new int[len];
//记录回文中心
int longestHalf = 0;
for(int i=0;i<len;i++){
//是否需要中心扩展
boolean needCalc = true;
//如果在右边界的覆盖之内
if(rightSide>i){
//计算相对rightSideCenter的对称位置
int leftCenter = 2*rightSideCenter-i;
//根据回文性质得到的结论
halfLenArr[i]=halfLenArr[leftCenter];
//如果超越了右边界,进行调整
if(i+halfLenArr[i]>rightSide){
halfLenArr[i]=rightSide-i;
}
}
//如果根据已知条件计算得出的最长回文小于右边界,则不需要扩展了
if(i+halfLen[leftCenterl]<rightSide){
//直接推出结论
needCalc=false;
}
//中心扩展
if(needCalc){
while(i-1-halfLenArr[i] >= 0 && i+1+halfLenArr[i]<len){
if(str.charAt(i+1+halfLenArr[i]) == str.charAt(i-1-halfLenArr[i])){
halfLenArr[i]++;
}else{
break;
}
}
//更新右边界及中心
rightSide = i+halfLenArr[i];
rightSideCenter = i;
//记录最长回文串
if(halfLenArr[i]>longestHalf){
center= i;
longestHalf=halfLenArr[i];
}
}
}
//去掉之前添加的#
StringBuffer sb = new StringBuffer();
for(int i = center-longestHalf +1;i<+center+longestHalf;i+=2){
sb.append(str.charAt(i));
}
return sb.toString();
}