背景
1975年,一个叫Manacher的人发明了一个算法,Manacher算法(中文名:马拉车算法),该算法可以把时间复杂度降到到O(n)。
manacher解决的问题是:求最大的回文子串长度,例如“123211”的最长回文子串是“12321”,长度为4;
正常暴力算法要遍历每个字母,字符串一长就会超时,但其实暴力的方法做了些无用功,manacher就是对其的优化。
代码
http://acm.hdu.edu.cn/showproblem.php?pid=3068
题意:求最长回文子串长度
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=11e4+10;//相当于110000+10,e后是几就是几个零
char str[N<<1];//位运算,相当于N*2,为什么要两倍空间?
int p[N<<1];
int main()
{
while(scanf("%s",str)!=EOF&&strlen(str))//空字符退出
{
int len=strlen(str);
for(int i=len;i>=0;i--)
{
str[2*i+2]=str[i];
str[2*i+1]='#';
}
str[0]='$';
len=2*len;//2*len的位置对应原字符串的最后一个字符
int id=0,mx=0,res=0;
for(int i=0;i<=len;i++){
if(i<mx)
p[i]=min(p[2*id-i],mx-i);//这就是优化,其实真的不难
else
p[i]=1;
while(str[i+p[i]]==str[i-p[i]])
p[i]++;
if(p[i]+i>mx){
mx=p[i]+i;
id=i;
}
if(p[i]-1>res) res=p[i]-1;
}
/*
for(int i=0;i<=len;i++)
printf("%d -> %c -> %d\n",i,str[i],p[i]);
*/
printf("%d\n",res);
}
return 0;
}
现在有很多问题,先不着急讲manacher所做的优化,先思考怎么找回文串,回文串分奇回文(aba)和偶回文(abba),这就要分情况讨论了,注意我们现在只是以暴力的思路来写,不用考虑别的,我们可以把所有的字符串改成奇回文的形式,
例如:“abba”-->“#a#b#b#a#”
很明显这样做可以把所有的字符串都变成只考虑奇回文的情况,之后我们设一个数组p来存放回文串长度,先全赋值为1,再找最大长度。
while(scanf("%s",str)!=EOF&&strlen(str))
{
int len=strlen(str);
for(int i=len;i>=0;i--)
{
str[2*i+1]=str[i];
str[2*i+2]='#';
}
str[0]='#';
len=len*2;
for(int i=0;i<len;i++){//这和上面的代码没关系
p[i]=1;
while(str[i+p[i]]==str[i-p[i]])
p[i]++;
}
for(int i=0;i<=len;i++)
printf("%d -> %c ->%d\n",i,str[i],p[i]);
}
这样子好像没什么问题,但我们输入“aaaa”就发现问题了;
4对应的“#a#a#a#a#”应该是5才对啊,为什么是6呢?这其实是数组越界,我们再while中加上一些判断,看指向到了什么地方
if(p[i]==5)
printf("i+p[i]=%d i-p[i]=%d\n",i+p[i],i-p[i]);
很明显在触碰边界时并没有停止,那为什么不继续等于-2,-3呢?这我也不清楚,总之会越界对吧,所以我们应该判断一下防止越界,具体做法就是在最前端加个独一无二的字符表示边界,为了保险,可以在末尾加上‘\0’,实际上不加这个末尾也行,如果最后又出错,不妨看看是不是这个原因,我还没碰到内种情况。还要注意不要换成“$a#b#b#a#”这样会破坏结构,我们只是要防止越界而以。
优化
到这,暴力的方法算是成型了,但是manacher怎么优化的呢?首先理解 i+p[i] 是最右的边界,因为从左到右i递增,所以 id+p[id] 可以说是id的领域,在这个领域里有什么不同?这就是关于id左右对称,我们如果先得到左边的p,到右边的对称点不就情况一致吗(先不考虑之后的字符影响),但是如果左边还受到超出领域的字符的影响就不是了,那就尽可能的取最大的,就是左边的位置减去左边界(等于右边界减去右边的位置),结合代码,这就是( p[ id - ( i - id ) ] )和 ( mx - i )
马拉车就是做了个这么的优化,理解了这些就可以去看看别的题目找找感觉了,这是最基础的。