算法应用 :解决字符串中的回文串问题
其中涉及到的结构:
(1)回文直径:以一个中心,向左右两边扩张,扩出来的区域总长 (2)回文半径:为回文直径一半的长度 (3)最右回文右边界:R(只要有回文串的长度 的右边界 > R 那么R就会扩展)
算法思路:
(1)先将给定的字符串进行处理,各个字符之间和字符串的首尾添加上“#”字符 (避免给定的字符串需要区分长度为奇数或偶数的可能性)
(2)对处理好的字符串进行遍历 求出每个位置的最长回文半径 通过遍历这一遍即可得出 最长回文子串的长度
(3)到每个位置求最长回文半径 以当前位置为中心点的回文半径大于R(最右回文右边界) 那么R就向外扩,且中心点 C 改为当前位置i 只要有一次不同那么就终止过程
向外扩张的过程中分为四种可能:
<1>当前位置 i 在 R 的外侧 那么 R 直接向外扩
<2>当前位置 i 在 R 的内侧 若 i'(为 i 在当前的整个回文边界中通过中心点映射的位置)的整个回文直径 在当前中心点 C 整个回文边界内(不在边界上 < L)那么 i 的回文半径就是 i'的 回文半经
<3>当前位置 i 在 R 的内侧 但是 i’ 的左边界超出了中心点 C 的左边界(i'的L < C的L)那么只需要判断当前R的下一个是否跟映射到i‘位置的字符相同即可 直接向外进行扩张
<4>当前位置 i 在 R 的内侧 但是 i’的左边界 跟 C的左边界 存在重合的问题 那么就需要对R的下一个位置 和以i为中心 以i‘的回文半径的长度 向i的左边进行比对 若比对成功则向右扩
举例 :
求出最大回文子串的长度
// 对给定的字符串进行处理 给字符串的首尾 和 各个字符中间添加上“#”
public static char[] manacherString(String s) {
char[] chars = s.toCharArray();
int index = 0;
char[] str = new char[s.length() * 2 + 1];
for (int i = 0; i < str.length; i++) {
// 判断i是为奇数还是为偶数 若为奇数则添加“#” 若为偶数则添加字符
str[i] = (i & 1) == 0 ? '#' : chars[index++];
}
return str;
}
// 对处理好的字符串进行manacher
// 求出最大回文子串
public static int maxLcpsLength(String s) {
if (s.length() == 0 || s == null) {
return 0;
}
char[] res = manacherString(s);
// 存储回文半径的数组
int[] half = new int[res.length];
// 回文中心点C
int C = -1;
int R = -1;//最右回文右边界再向右的一个位置 整个的回文有效区域为 0..R-1 位置
int max = Integer.MIN_VALUE;//求出的最大回文子串的长度
for (int i = 0; i < half.length; i++) {
// 先对当前 i 位置 求出最小的回文子串的长度
// 找出不用验证的长度(节省时间复杂度)
// 四种可能均可用当前的
// (1)i > R 直接暴力扩张
// (2)i < R 而且i‘整个边界均在C的整个边界中 那么i = i'的回文边界长度(half[2*C-i]代表着 i'的回文半径长度)
// (3)i < R 而且i‘的左边界跟C的左边界重合 那么i = i'的回文边界长度
// (4)i < R i’的左边界在C的左边界外 那么 i = R-i(当前位置到最右回文边界)
half[i] = R > i ? Math.min(half[2 * C - i], R - i) : 1;
// 让每一种可能性的 i 都去进行比对
// 没有超过边界
while (half[i] + i < s.length() && i - half[i] > -1) {
// 在i的左右两边进行判断是否为相同的 如果相同那么回文半径++
if(half[i+half[i]] == half[i-half[i]]){
half[i]++;
}
else {
// 只要有一次不匹配那么就终止匹配
break;
}
}
// 对R和C进行更新操作
// 判断当前的i+half[i]是否大于当前的R
if(i + half[i] > R){
// 如果大于R那么就执行更新操作
R = i+half[i];
C = i;
}
// 对每个位置求完后都进行判断 看看是不是比之前求出的最大回文半径更大了
max = Math.max(max,half[i]);
}
// 最大回文半径 与 最大回文子串之间的关系 就是
// 最大回文半径 - 1 = 最大回文子串长度
return max-1;
}