您的位置:首页 > 其它

2016.8.6 manacher算法(最长回文串)

2016-08-07 08:38 246 查看
  给出一个字符串,要求出其中最长的回文串的长度。

  最暴力的方法,枚举开头和结尾,再检查一遍,时间复杂度O(n^3)。

  第二种,利用回文串的性质,以中心为对称轴,左右两边对称,所以枚举中间点,再向两边找,比较是否相同(可能有两个数作为中心,要在每两个数中插入一个相同的特殊符号即可),时间复杂度为O(n^2)。



有三种情况:

  ①大回文串s2中左半边包含s1回文串,因为回文串的对称性,所以在对应位置的s3的最长回文串长度至少为s1的长度,又因为s1的最长回文串长度为3,没有到达s[1]、s[5]位置,所以这两个位置的字符一定不相同。所以s[5]!=s[9],所以,如果对应回文串的位置没有到达大回文串的边界,那么一定不用再向两边扩展。

  ②大回文串s4左半边包含s3,所以s5最长回文串长度至少为3。而s3刚好在s4的边界,不属于①,再看到实际情况,因为边界以外不再属于s4,所以并不知道s[9]和s[13]是否,结果发现s5是可以向两边扩展的。所以,如果对应回文串的长度刚好到达大回文串的边界,那么要尝试向两边扩展。

  ③这种情况没画图,但与②类似,当左半边所包含的回文串中心的范围已经超过大回文串,因为大回文串的外部情况是未知的,所以对应位置的最长回文串长度至少为所包含的回文串中心到大回文串边界的距离x2+1。

  还有一个问题,如何确定大回文串。其实,越靠后的回文串对后面的贡献越大,因为可以将前面的值传到更后面。

  这个算法的时间复杂度为O(len),因为每次向两边扩展右边的字符都必定是以前没有找过的,所以多找s.length()次。

//注意:要像第二种方法一样,每两个字符之间插入一个相同的特殊符号。


代码:

#include <iostream>
#include <fstream>
#include <string.h>

using namespace std;

int n,p;
int ans[2*110006];
int Max=0;
char c[2*110006],s[2*110006];

int main()
{
scanf("%d",c);
Max=0;
n=0;
s[n++]='@';
for (int i=0;i<strlen(c);i++)
s[n++]='#',s[n++]=c[i];
s[n++]='#';
s[n++]='^';
ans[0]=ans[1]=ans[2]=0;
p=2;
for (int i=3;i<n;i++)
{
ans[i]=max(0,min(ans[2*p-i],p+ans[p]-i));
while (s[i+ans[i]+1]==s[i-ans[i]-1]) ans[i]++;
if (ans[i]+i>ans[p]+p) p=i;
}
for (int i=0;i<n;i++) Max=max(Max,ans[i]);
printf("%d\n",Max-1);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  manacher