首先我们解决下奇数和偶数的问题,在每个字符间插入"#",并且为了使得扩展的过程中,到边界后自动结束,在两端分别插入 “^” 和 “$”,两个不可能在字符串中出现的字符,这样中心扩展的时候,判断两端字符是否相等的时候,如果到了边界就一定会不相等,从而出了循环。经过处理,字符串的长度永远都是奇数了。
首先我们用一个数组 P 保存从中心扩展的最大个数,而它刚好也是去掉 “#” 的原字符串的总长度。例如下图中下标是 6 的地方。可以看到 P[ 6 ] 等于 5,所以它是从左边扩展 5 个字符,相应的右边也是扩展 5 个字符,也就是 “#c#b#c#b#c#”。而去掉 # 恢复到原来的字符串,变成 “cbcbc”,它的长度刚好也就是 5。
求原字符串下标用 P 的下标 i 减去 P [ i ],再除以 2 ,就是原字符串的开头下标了。例如我们找到 P[ i ] 的最大值为 5 ,也就是回文串的最大长度是 5 ,对应的下标是 6 ,所以原字符串的开头下标是 (6 - 5 )/ 2 = 0 。所以我们只需要返回原字符串的第 0 到 第 (5 - 1)位就可以了。求每个 P [ i ]接下来是算法的关键了,它充分利用了回文串的对称性。我们用 C 表示回文串的中心,用 R 表示回文串的右边半径坐标,所以 R = C + P[ C ] 。C 和 R 所对应的回文串是当前循环中 R 最靠右的回文串。让我们考虑求 P [ i ] 的时候,如下图。用 i_mirror 表示当前需要求的第 i 个字符关于 C 对应的下标。我们现在要求 P [ i ], 如果是用中心扩展法charAt(i+p[i]+1) == charAt(i-p[i]-1),那就向两边扩展比对就行了。但是我们其实可以利用回文串 C 的对称性。i 关于 C 的对称点是 i_mirror ,P [ i_mirror ] = 3,所以 P [ i ] 也等于 3 。
另:
对于奇数长度的回文字符串,其中间点为中间的字符;对于偶数长度的回文字符串,其中间点在中间两个字符之间。现假设我们要暴力找一个字符串所有的子字符串,则其所有可能成为子字符串中间点的位置如下(字符之间的位置用竖线表示):
例如position=2处,左边是a,右边是b,p[2]=0;position=3处,以其为中心的最长回文字符串为’aba’,所以p[3]=3。另一方面,如果把竖线也当作一个字符,则很容易证明p还有另一种解释:以该position为中心的LPS,向两边延展的步数。例如position=3处,从b向左向右各3步,得到’|a|b|a|’,为回文串,因此p[3]=3;position=6处,从|向左向右各6步,得到’|a|b|a|a|b|a|’,为回文串,因此p[6]=6。故p[i]即为回文字符串长度
public String preProcess(String s) {
int n = s.size();
//处理S字符串使得ret = ^#...#$
if(n==0)
return "^$";
int []p = new int[n];
string ret = "^";
for(int i = 0; i < n: i++)
{
ret = "#" + s.charAt(i);
}
ret += "#$";
return ret;
}
// 马拉车算法
public String longestPalindrome2(String s) {
String T = preProcess(s);
int n = T.length();
int[] P = new int[n];
int C = 0, R = 0;
for(int i = 1; i < n-1: i++)
{
int i_mrrior = 2*C-i;//i_mrrior关于c与i对称(c=(i+i_mrrior)/2)
if(R>i)
{
p[i] = math.min(R-i,p[i_mrrior]); //防止溢出
}
else
{
p[i] = 0;
}
while(charAt(i+p[i]+1) == charAt(i-p[i]-1))//中心扩展法,因为p[i]+i就已经表示回文串最右边半径坐标了,此时再往右移一步,判断下一个点是否相等
{
p[i]++;
}
//更新C和R
if(R<p[i]+i)
{
R = p[i]+i;
C = i;
}
}
//找出P的最大值
int maxLen = 0; //p[c]
int centerIndex = 0;//c
for(int i = 1; i < n - 1; i++)
{
if(maxLen<p[i])
{
maxLen = p[i]
centerline = i;
}
}
int begin = (centerline - maxLen)/2;
return s.substring(begin,begin+maxLen);
}