马拉车算法
算法定义:
Manachar算法主要是处理字符串中关于回文串的问题的,它的强大之处在于,可以在O(n)的时间复杂度中解决回文串的问题。
主要参考:
马拉车算法
算法思想:
- 解决回文串的奇偶性的问题
主要办法:在每个字符间插入"#",并且为了使得扩展的过程中,到边界后自动结束,在两端分别插入 “^” 和 “$”
此时字符串长度为 2*n+3。
T数组:变换后的数组
P数组:掉 “#” 的原字符串的总长度
我们以下图为例:
P[ 6 ] = 5;代表的意思为:左边扩展 5 个字符,相应的右边也是扩展 5 个字符,也就是 “#c#b#c#b#c#”。而去掉 # 恢复到原来的字符串,变成 “cbcbc”,它的长度刚好也就是 5
- 求原字符串的下标
我们需要借助P数组,比如:求P[i] = 5 的原字符串的下标。index = 6;(index - p[i])/2 = 0;
因此,只需要返回原字符串的0~4的下标。
综上:公式为:(index - p[i])/2 ~ P[i] -1。 - 求P数组
求P数组是算法的核心。首先,我们做如下定义:
1)C:回文串的中心
2)R:回文串的右边半径,即:R = C + P[i]。就是当前回文串中心,最右边
3)i_mirror :当前需要求的第 i 个字符关于 C 对应的下标
此时,我们求P[i],可以利用回文串的对称性,P[i] = P[i_mirror]。但是这个并不是绝对的。
考虑如下几种特殊情况:- p[i_mirror] 较大,超过了R的边界。
- p[i_mirror] 较大,超过了R的边界。
当我们要求 P [ i ] 的时候,P [ mirror ] = 7,而此时 P [ i ] 并不等于 7 ,为什么呢,因为我们从 i 开始往后数 7 个,等于 22 ,已经超过了最右的 R ,此时不能利用对称性了,但我们一定可以扩展到 R 的,所以 P [ i ] 至少等于 R - i = 20 - 15 = 5,会不会更大呢,我们只需要比较 T [ R+1 ] 和 T [ R+1 ]关于 i 的对称点就行了,就像中心扩展法一样一个个扩展。
此时,我们同时需要更新 C 和 R 为当前的回文串了。因为我们必须保证 i 在 R 里面,所以一旦有更右边的 R 就要更新 R。
- p[i_mirror] 较小,触发了原字符串的左边界。
此时P [ i_mirror ] = 1,但是 P [ i ] 赋值成 1 是不正确的,出现这种情况的原因是 P [ i_mirror ] 在扩展的时候首先是 “#” == “#” ,之后遇到了 "^"和另一个字符比较,也就是到了边界,才终止循环的。而 P [ i ] 并没有遇到边界,所以我们可以继续通过中心扩展法一步一步向两边扩展就行了。
- i=R,此时P[i] = 0
代码
public String longestPalindrome(String s) {
return longest(s);
}
private String longest(String s) {
String temp = preProcess(s);
int n = temp.length();
int[] P = new int[n];
int C = 0;
int R = 0;
for(int i = 1;i<n-1;i++){
if(i < R){
int i_mirror = 2*C - i;
P[i] = Math.min(P[i_mirror],R-i);
}else{
P[i] = 0;
}
while(temp.charAt(i+P[i]+1) == temp.charAt(i-P[i]-1)){
P[i]++;
}
if(i + P[i] > R){
C = i;
R = i+P[i];
}
}
int maxLen = 0;
int curIndex = 0;
for(int i = 0;i<n;i++){
if(P[i] > maxLen){
curIndex = i;
maxLen = P[i];
}
}
int begin = (curIndex - maxLen)/2;
return s.substring(begin,begin + maxLen);
}
public String preProcess(String s){
if(s.length() == 0){
return "^$";
}
String res = "^";
for(int i = 0;i<s.length();i++){
res += ("#" + s.charAt(i));
}
res += "#$";
return res;
}
测试代码
public static void main(String[] args) {
String test = "babad";
Exec005 a = new Exec005();
String s = a.longestPalindrome(test);
System.out.println(s);
}