回文串就是一个字符串从左到右和从右到左是一样的,或者说他是关于中心对称的。
如何找出一个字符串中最长的回文子串呢(假设只有唯一的最长回文子串)?Manacher算法给出了O(n),的解法。
字符串的个数,偶数与奇数时判断有不同,为了简化,给每一个字符加一个特定的字符,这样就可以保证字符串始终是基数,如:#a#, #a#a#
其次需要一个idx,一个max和一个数组p,mx记录了当前所有回文串中最右边的位置,id就是这个回文串的中心位置,p[i]记录了一str[i]为中心的回文串的半边长。如下所示:
# a # b # c # c # d #
p[i] 1 2 1 2 1 2 3 2 1 2 1 当访问到b的时候,mx=4, id=3。求得p,也就求得了结果。
在求p[i]时,p[i]的初始化有两种情况: 结合参考图来理解
- mx <= i,p[i] = 1;
- mx > i, p[i] = min(p[2*id - i], mx - i + 1)
之后在以i为中心,p[i]为边长想两端扩展,判断回文。在判断的时候需要注意不要越界,因为有结束符‘\0'存在,所以只需考虑起点就行。
string Manacher(string s) {
string s2 = "#";
for(int i = 0; i < s.size(); i++) //变化字符串
s2 = s2 + s[i] + "#";
int mx = 0, mid = 0;
int *p = new int[s2.size()];
for(int i = 0; i < s2.size(); i++){
p[i] = mx > i ? (p[2 * mid - i] > mx - i+1 ? mx - i+1 : p[2 * mid - i]) : 1;
while(i - p[i] >= 0 && s2[p[i] + i] == s2[i - p[i]]) //这里继续完善p[i]
p[i]++;
if(p[i] + i - 1 > mx){ //定位mx
mx = p[i] + i - 1;
mid = i;
}
}
int max = 0, index = 1;
for(int i = 0; i < s2.size(); i++){ //找出最长的子串,并记录id
max = (p[i] > max) ? p[index = i] : max;
}
delete[] p;
return s.substr((index - max + 1) / 2, max - 1); //截取最长回文子串
}
来源:CSDN
作者:shzxrt
链接:https://blog.csdn.net/shzxrt/article/details/27579877