这是一种 时间复杂度为
的高效的求解字符串回文子串的动态规划算法。
网上文章一大堆,我主要参考 : 最长回文子串——Manacher 算法
他的思想如下:
算法描述
对一个字符串,str,(e.g: str = “agga”) 由于他的回文子串有两种情况,1是以字符串中字母为中心,即长度为奇数的串,2是以间隔为中心,即长度为偶数的串。因此先对其做一个填充:
str_pad = “#a#g#g#a#”, 这样回文串就全部以字符为中心了。
记 以字符 为中心的最长回文串的半径。显然(R[i]-1) 就是原来的回文串长度。
- 遍历
计算所有的
,
记 为当前遍历的字符串中回文串的最右端点, 同时记 为当前最右端点的回文串的中心,即 一定是一个回文串。那么当前计算的点 有两种情况:
a. , 此时它关于 的对称点就是 。由对称性: 因此我们可以继续从此时的 往后匹配.(并且仅有 时才会往后匹配,即mr移动)
b. , 此时就挨着匹配就好
code
上面的描述有点简短,建议结合文首文章中的图片来理解
char str[MAXN],pp[MAXN*2];
int R[MAXN*2]; // 回文串半径
int n,len;
void manacher(){
// preprocess
n = strlen(str+1);
len=1;
char pad = '#';
pp[0] = '$';
for(int i=1 ; i <= n ; ++i){
pp[len++] = pad;
pp[len++] = str[i];
}
pp[len++] = pad;
ms(R,0);
int mr=0,zx =0;//mr 刚好越过最右回文的点
for(int i=1 ; i<len ; ++i){
R[i] = mr > i? min(R[2*zx -i],mr -i) : 1;
while (pp[i+R[i]]==pp[i-R[i]])++R[i];
if(i+R[i]> mr){
mr = i+R[i];
zx = i;
}
}
}
复杂度分析
我们可以利用摊还分析,由于只有mr 移动才会开始有效匹配,因此, 移动次数就是 时间复杂度