package jinjie_class_01;
import java.util.Arrays;
/**
* Manacher算法
* @author lenovo
*
*/
public class Code_04_Manacher {
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++) {
// i & 1 可以用作判断奇数跟偶数 两个操作数中位都为1,结果才为1,否则结果为0
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 != charArr.length; i++) { // 求每一个i位置的回文中心
// >> 1. i 如果在 R的外面,那么就是暴力继续往外扩
// 当 i 在 R的外面,或者 i 就在 R 上,就认为我不用扩的区域就只有一个 就是自己
// >> 2. i 在R里面, 为了加速,认为地分为三种情况
// 三种情况: a.i`的回文左右半径在L与R之间 b. i`的回文左半径超过了 L c. i`的回文左半径在L上(压线)
// i`的回文和i到R的距离 哪个小, 哪个就起码是i位置的回文半径 (这个半径还可能继续往外扩,现在得到的长度是起码的长度)
// index是取得最右边界的回文中心点
// 当 i 在回文右边界的里面,就起码有一个不用去验证的区域,不用验证,就知道这个块区域是回文的
/**
* 此时得到的pArr[i] 只是起码不用验证的区域 (一部分回文区域)
*/
pArr[i] = pR > i ? Math.min(pArr[2 * index - i], pR - i) : 1;
/**
* 可以按照分析地那样,分出四种情况 (两种大范围的情况)
* 情况1. i 如果在 R的外面
* 情况2. i 在R里面 i`的回文左右半径在L与R之间
* 情况3. i 在R里面 i`的回文左半径超过了 L
* 情况4. i 在R里面 i`的回文左半径在L上(压线)
*/
// 老师的代码,有一个起码不用验的区域之后,还让你扩一下,如果是情况2跟情况3 O(1) 直接出结论这两种情况的话,那么继续往外扩一下,就会失败
while (i + pArr[i] < charArr.length && i - pArr[i] > -1) { // 先判断 需要验证的区域 左边 跟 右边 是否越界
if (charArr[i + pArr[i]] == charArr[i - pArr[i]]) // 扩出来的左右两边位置相等 回文半径 就 +1
pArr[i]++;
else {
break;
}
}
if (i + pArr[i] > pR) { // 扩出来的区域超过了之前的最右回文边界
pR = i + pArr[i]; // 更新最右回文边界
index = i; // 更新最右回文边界的中心点
}
max = Math.max(max, pArr[i]); // 记录全局最大值
}
System.out.println(Arrays.toString(pArr));
return max - 1;
}
public static void main(String[] args) {
String str1 = "abc1234321ab";
System.out.println(maxLcpsLength(str1));
}
}
Manacher算法求解字符串的最长回文子串
猜你喜欢
转载自blog.csdn.net/qq_38200548/article/details/82048784
今日推荐
周排行