今天在做leetCode的https://leetcode.com/problems/palindromic-substrings/description/这道题时遇到了一个很有趣的算法,Manacher’s Algorithm (俗称马拉车算法,哈哈),看leetCode的解法时有些细节没有弄懂,看下面这篇文章得到了解答,感谢作者!!!他是用C++实现的,我在结尾附上了我自己写的Java实现,有详尽的代码注释。
文章链接如下:
http://www.cnblogs.com/bitzhuwei/p/Longest-Palindromic-Substring-Part-II.html
有兴趣的朋友可以去看看,很有意思的算法。
以下是我的JAVA实现
//有一点值得说明一下,T的首尾字符(不允许设置为#),作者只是简单的提了一下,边界检查,我想了一下,我们实际上遍历是从i = 1到 len - 2,略去了首尾字符,而这两个字符的作用就在于,让我们在i = 1和i = len - 2的时候T[i - p[i] - 1]和对应的T[i + p[i] + 1]不会越界(这也是为什么不允许设置为#的原因)。
class Solution {
public int countSubstrings(String s) {
int len = s.length() * 2 + 3;
char[] T = new char[len];
T[0] = '@';
T[1] = '#';
int t = 2;
for(char c : s.toCharArray()){
T[t++] = c;
T[t++] = '#';
}
T[t] = '$';
int[] p = new int[len];
//中心,中心右边界
int C = 0,R = 0;
for(int i = 1;i < len - 1;i++){
//与i对称的i'
int mirror = C - (i - C);
//i与右边界的距离
int diff = R - i;
//若i落在中心与右边界之间
if(diff >= 0){
//若p[mirror] < diff,说明可以使用对称的特性,p[i] = p[i'],否则将p[i]赋值为diff,之后须要遍历求得p[i]
if(p[mirror] < diff){
p[i] = p[mirror];
}else{
p[i] = diff;
while(T[i + p[i] + 1] == T[i - p[i] - 1])p[i]++;
C = i;
R = i + p[i];
}
}else{
//若落在右边界之外,则计算p[i],更新中心和右边界
p[i] = 0;
while(T[i + p[i] + 1] == T[i - p[i] - 1]) p[i]++;
C = i;
R = i + p[i];
}
}
int sum = 0;
//利用p数组求得sum,公式可以这样理解,如果p[i]为5,那么可以得到长度分别为1,3,5的回文子串,也就是(5 + 1) / 2 = 3
for(int i : p) sum += (1 + i) / 2;
return sum;
}
}