Manacher算法
1.应用
给定一个字符串s,求s中最长回文子串的长度;(一系列与回文串有关的问题)
2.经典方法求回文子串
s a12b->str #a#1#2#b#
注意点:不一定用#,其他任意字符均可,因为我们总是用虚的位置(填充字符匹配填充字符)匹配虚的位置的
对于str我们求出每个位置的回文长度数组pArr
#a#1#2#b#
index 012345678
pArr 131313131
结果为 max(pArr)/2
时间复杂度 O(n^2)
3.概念引入
1.回文半径/直径
str #a#1#2#b#,我们定义回文半径数组pArr
pArr 121212121
直径 131313131
2.回文半径扩充过程的最右边界R,中心点C
str #1#2#2#1#
index 012345678
当i=0时:[0,0],R=0 ,C=0
当i=1时:[0,2],R=2 ,C=1
当i=2时:[2,2],R=2 ,C=2
当i=3时:[2,4],R=4 ,C=3
当i=4时:[0,8],R=8 ,C=4
...
可以看出C与R是伴生关系,当R变化的时候C是一定会发生变化的
3.Manacher流程
为了方便理解,以下定义的变量
i:遍历到i的位置,以i位置为中心,求i的最大回文半径
R:之前的i在遍历过程中扩充到的最右的位置(初始值为-1)
L:之前的i在遍历过程中扩充到的最右的位置时的起始位置
C:之前的i在遍历过程中扩充到的最右的位置时的中心点的位置(初始值为-1)
i‘:以C为中心做的i的对称位置
以上概念不理解没关系,我们通过流程来解释清楚
1.当i>=R时,暴力扩
str #1#2#1#
0123456
当i=0时,此时i>R,我们使用经典方法扩,发现走不下去(越界),更新R=0,C=0
当i=1时,此时i>R,我们使用经典方法扩,发现走不下去(越界),更新R=2,C=1
当i=2时,此时i==R,我们使用经典方法扩,发现走不下去(1!=2)
2.当i<R时
分析一般情况
str # 1 # 2 # 2 # 1 #
L i' C i R
1.当i‘的区域在L…R回文区域里的时
str a b c b d k s k d b c b a(这里为了方便去掉#字符)
L i‘ C i R
此时i的回文半径就等于i’的回文半径
证明
[...x(..?..)y...C..z(..?..)k...]//?即代表i的位置,()区域为对应的回文直径
L C R
x!=y x=z y=k ->z!=k
2.当i‘的区域在L…R回文区域外的时
str (a b c d e d c b a) k a b c d e d c f t
L i' C i R
此时i的回文半径等于R-i
证明
(...z[..i'..k..)..C....x(..i..)]y.//()为i‘扩充出来的区域,[]为此时最大R,C的区域
k==x z==k 如果还能扩 x==y,如果x==y那么y==z,那就说明[]回文区域求错了
3.当区域压线时
str a b c d c b a k s k a b c d c b a ?
L i' C i R
a b c d c b a这一块是不需要验的,从需验证k是否等于? 开始
3.代码实现
public class Manacher {
public static char[] manacherString(String str){
char[] charArr = str.toCharArray();
char[] res=new char[charArr.length*2+1];
int index=0;
for (int i = 0; i < res.length; i++) {
res[i]=(i&1)==0?'#':charArr[index++];
}
return res;
}
public static int maxLcpLength(String str){
if(str==null || str.length()==0){
return 0;
}
char[] charArr = manacherString(str);
int[] pArr=new int[charArr.length];
int R=-1;
int C=-1;
int res=1;
for (int i = 0; i < pArr.length; i++) {
//统一处理不需要配对的长度
pArr[i]=R>i?Math.min(pArr[2*C-1],R-i):1;
while(i+pArr[i]<charArr.length && i-pArr[i]>=0){
if(charArr[i+pArr[i]]==charArr[i-pArr[i]]){
pArr[i]++;
}else{
break;
}
}
if(i+pArr[i]>R){
R=i+pArr[i];
C=i;
}
res=Math.max(res,pArr[i]);
}
return res-1;
}
public static void main(String[] args) {
String res="abcdfrrfdcba";
System.out.println(maxLcpLength(res));
}
}