简介:
一个字符串中找到最长回文子串。
求解:
暴力解法:
分奇数回文和偶数回文。奇回文简单,只需从一个位置向两边扩,对于偶回文,需要添加字符来解决偶回文的问题。(中间加的字符可以是任何字符,即使原字符串中含有的也可以。)
例如:字符串为11311;其中奇偶均有,需添加字符变成#1#1#3#1#1#。
则最大回文长度为11/2等于5.
时间复杂度:O(n*n)
Manacher:
部分概念:
回文直径、回文半径:从某个位置开始扩,所扩出的回文的直径和半径。
arr回文半径数组:记录每个位置的回文半径。通过前面计算出来的加速后面的求解。
最右回文右边界R:所有位置扩出的回文中最右到达的位置。
回文右边界中心C:如果有多个均可以到达R,C记录最早到达R的中心
求解i位置的回文半径:分四种情况
**
可能性1:
**i位置不在回文右边界里面,则暴力破
可能性2:
i位置在回文右边界里面并且,i关于C的对称点i1的回文边界在LR之间
例子:
此时i和i1的回文半径一样(由i与i1关于C对称很容易推出);
可能性3:
i 位置在回文右边界里面并且,i关于C的对称点i1的回文边界不在LR之间
例:
此时 i 的回文半径为 i 到R。
可能性4:
i1回文半径和L压线。
此时i到R不用判断,需要从R向外扩,看是否相匹配。
时间复杂度: O(n)
代码实现(java)
public class 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++){
res[i] = (i & 1) == 0 ? '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 C = -1;
int R = -1;
int max = Integer.MIN_VALUE;
for (int i = 0; i != charArr.length; i++){
pArr[i] = R > i ? Math.min(pArr[2*C-i],R-i ) : 1;
while(i + pArr[i] < charArr.length && i - pArr[i] > -1){ //没分情况2、3,均需要执行一下while循环,只不过2、3就一执行次
if (charArr[i +pArr[i]] == charArr[i - pArr[i]])
pArr[i]++;
else
break;
}
if(i + pArr[i] > R){
R = i + pArr[i];
C = i;
}
max = Math.max(max,pArr[i]);
}
return max - 1;
}
public static void main (String[] args){
}
}
例题:
题目:只向字符串最后添加字符,使得新串为回文串,并且添加字符最少;
例如:原串为abc12321;答案为新串abc12321cba
求解:即为求包含最后一个字符的最长回文串,前面不是的,逆序,添加到最后,组成的新串即为答案。manacher算法中,一旦R到达最后一个位置,便停止。
左神算法进阶班笔记