以上的几篇博客推荐给大家,嘿嘿
Manacher算法是查找一个字符串的最长回文子串的线性算法。
回文的定义:正反读都是一样的字符串叫做回文串。如:madam,lol,oppo,zz,甚至连单字符都可以被称为回文(串)。
上面的例子可以看出,回文串可以分为两种:奇数回文和偶数回文。
Manacher算法可以将长度为奇数和偶数的回文串一起考虑:在原字符串的相邻字符串之间插入一个分隔符,字符串的首尾也要分别添加,注意分隔符必须是原字符串中没有出现过的,最好在第一个#之前和最后一个#之后再加上一个特殊字符(而这不一样)以防越界
Len数组的介绍(Len[i] 又可以称为该点的回文半径)
Len数组有一个性质,那就是Len[i]-1就是该回文子串在原字符串S中的长度,至于证明,首先在转换得到的字符串T中,所有的回文字串的长度都为奇数,那么对于以T[i]为中心的最长回文字串,其长度就为2*Len[i]-1,经过观察可知,T中所有的回文子串,其中分隔符的数量一定比其他字符的数量多1,也就是有Len[i]个分隔符,剩下Len[i]-1个字符来自原字符串,所以该回文串在原字符串中的长度就为Len[i]-1。
根据之前已经匹配好的最大的回文半径R,设当前点的回文半径为r,r的起点一定大于R的起点,因为在处理r的时候,R已经处理好了
根据对称性,找到r1,从而可以确定r的回文半径
红色和红色对对称,蓝色和蓝色对称,所以,r和r1仍然关于R中心点对称,也就是r的回文半径依旧等于r1的回文半径.
所以黄色的部分还需要继续匹配下去,来判断是否需要扩大R
可以用VS单步调试,看一下s数组,mx,pos,Len数组的变化
void init()
{
int i,k = 0;
s[k++] = '@';
len = strlen(ans);
for(i=0;i<len;i++)
{
s[k++] = '#';
s[k++] = ans[i];
}
s[k++] = '#';
s[k++] = '$';
s[k] = '\0';
len = k;
}
int Manacher()
{
init();
int mx=0,pos,cnt=1;
for(int i=1;i<len;i++)
{
if(mx>i)
Len[i]=min(Len[2*pos-i],mx-i);
else
Len[i] = 1;
while(s[i+Len[i]]==s[i-Len[i]])
Len[i]++;
if(Len[i]+i>mx)
{
mx = Len[i]+i;
pos = i;
}
cnt = max(Len[i],cnt);
}
return cnt-1;
}