Manacher算法是时间复杂度为O(n)的求最长回文子串的算法
算法的主要思想是从左到右处理字符串,求每个位置为中心的两端对称的最大半径。
由于我们只考虑以每个位置为中心,所以要把字符串转换一下,如字符串babbcaa,转换成@#b#a#b#b#c#a#a#$,这样就可以统一处理了。
void Manacher(char s[],int n,int radius[])
{
int i,j;
for(i=1;i<n;i++) radius[i]=1;
for(i=2;i<n;i=j)
{
while(s[i-radius[i]]==s[i+radius[i]]) radius[i]++; //求以当前位置为中心的最大对称半径
int rBnd=i+radius[i];
for(j=i+1;j<rBnd;j++) //对当前位置半径覆盖范围内右边的位置进行处理
{
/*
** 如果位置j以位置i为中心的对称位置k=i-(j-i),它的最大半径被i的半径包含,
** 那么j的最大半径就等于radius[k],根据对称性;
** 否则,也就是位置k的最长回文串的左端等于或者向左超过以i为中心的回文串的左端,
** 那么j的最大半径至少为i+radius[i]-j;
** 这样,i~j中间位置的最大半径都求出来了,i下次直接从j开始扩展最大半径
*/
if(j+radius[i-(j-i)]<rBnd)
radius[j]=radius[i-(j-i)];
else
{
radius[j]=rBnd-j;
break;
}
}
}
}
虽然表面是双重循环,但我们分析一下,由于循环内部是每次从位置i扩展到下一个位置j,一直扩展到位置n,如果把外层循环,一层一层展开接起来,那就是常数倍从1到n的运算,所以总起来的运算量是O(n)的。
网上较多的是下面这种写法:
void Manacher(int rad[],char str[],int n)
{
int i,mx=0,id;
for(i=1;i<n;i++)
{
if(mx>i) rad[i]=min(rad[2*id-i],mx-i);
else rad[i]=1;
for(;str[i+rad[i]]==str[i-rad[i]];rad[i]++)
;
if(mx<rad[i]+i)
{
mx=rad[i]+i;
id=i;
}
}
}
相关题目:poj 3974