刚开始写播客的时候比较呆,写过一篇关于Manacher算法的博客,回头望去,感觉条理不是很清晰,虽然代码在运行时不会出现什么大问题,但是,总让我感觉不舒服,所以,今天我就再来写一篇关于Manacher算法的文章。再谈谈对Manacher算法的理解。
首先,我这一想法来自于我在leetCode上刷题的时候碰到的“求最长回文数”,一题,但是我采用了中心拓展法求解,获得了成功,但是总感觉做“求最长回文数”这一类题,你不使用Manacher算法,总感觉心里空空的。所以我又用了几个小时,重温了一下Manacher算法。果然是“温故而知新”!
LeetCode上关于该题的描述:
首先我要说一句,不要把Manacher算法想的太高深,其实它的思想很简单,实现也很简单,只是需要在一些细节上特别注意。
在我看来,Manacher算法的核心就是,充分利用以已知信息。在这里,已知信息代表我们当前节点 “i”之前已经探测过的节点的最大回文子串信息。
本人整理的解体步骤如下:
下面是本人的代码,仅供参考:
public String longestPalindrome(String s) {
/**
* first
*
* 第一步,处理字符串,将所有的字符串的长度都变为偶数长度。
* 第二步,在字符串的首尾都加上待计算字符串中不存在的字符。标识始末位置
*/
char[] msg= DisposeStr(s);
/**
* second
* 第一步,设置两个变量
*
* mx:记录当前扩展到最远端的下标
* id:记录当前扩展到最远端的下标的中心点的id
*/
String result = travelArr(msg);
return result;
}
//处理字符串
private char[] DisposeStr(String str){
int len = str.length();
int i = 0 ;
char[] product = new char[len*2+3];
product[i++] = '$';
product[i++] = '#';
//插入分隔符,将字符长度偶数化
for(int j = 0 ; j < len ;j ++){
product[i++] = str.charAt(j);
product[i++] = '#';
}
product[i] = '%';
return product;
}
//遍历数组,寻找最长回文子串
private String travelArr(char[] arr){
int flag = 0;
int idx= 0;
int mx=0;//保存当前最大的扩展边界。
int id=1;//保存当前最大扩展边界的中心点。
int[] size = new int[arr.length];
//遍历当前数组,寻找最大回文串
for(int i = 1 ; i< arr.length ;i++){
//根据当前节点的前驱节点的最大回文数信息推算当前节点最大回文数信息的部分数据
size[i]=mx>i?(size[2*id-i]>(mx-i)?mx-i:size[2*id-i]):1;
//继续向两边扩展
while(size[i]+i <arr.length && i-size[i] >0 && arr[size[i]+i] == arr[i-size[i]]){
size[i]++;
}
//更新最右端节点和该节点对应的回文中心
if(size[i]+i > mx){
mx = i+size[i];
id = i;
}
//保存当前回文半径最大的数组下标,及最大回文长度,以便后续查看,输出
if(size[i]>flag){
flag =size[i];
idx =i;
}
}
//构造最长回文子串
StringBuffer sb = new StringBuffer("");
for(int i = idx-flag+1 ; i< idx+flag ; i++ ){
if(arr[i] != '#'){
sb.append(arr[i]);
}
}
//main方法进行测试
public static void main(String[] args) {
Manacher manacher = new Manacher();
System.out.println(manacher.longestPalindrome("eabadawdabasddddadddawdasda"));
}
谢谢观看。