【Manacher(马拉车)算法】

Manacher

英译过来就是马拉车

是一个求一个字符串中最长回文连续子序列的算法

例如abaabaabb最长的就是7

abaaaaba最长就是8

ababbb最长就是3

注意:一个字符串可能不只有一个最长回文连续子序列

传统做法就是枚举中间点向两边扩展,时间复杂度是O(N^2)

然后这个算法就优化到了O(N)

这是优化吗,性质都变了啊喂

算法实现

首先回文分为两种:奇回文偶回文如果枚举的话,有时候是字符,有时候要枚举字符之间的位置,就很麻烦

为了操作方便(因为我们要定义以i为中心),我们把它们都变成奇回文

怎么做呢?很简单,全部插入一个符号即可(没出现过)

例如  

abaababa ——> (#a#b#a#a#b#a#b#a#)

前后是防止越界,你可以随便定义(只要字符串里没有的,不会冲突)

原来aba就可以变成#a#b#a#

原来baab就可以变成#b#a#a#b#

这样一来就全部变成了奇回文啦!

我们定义P[i]是以i为中心的,最长回文串的半径(包括自己)

字符串 ( # a # b # a # a # b # a # b # a # )
P 1 1 2 1 4 1 2 7 2 1 4 1 2 1 4 1 2 1 1

我们现在要做的就是O(N)处理出每一个P[i]

考虑能不能根据之前已经得到的来推测出答案呢

 当前我们枚举到i,已知有一个以mid为中心的回文子串,其右端点到了mx

可知以mid为分界线,左右两边是完全对称的(回文子串定义)

当i+p[j]<mx时

 我们就可以直接更新答案,因为不可能更长了

当i+p[j]>=mx时

我们只能到mx这个位置,因为我们不知道mx后面是否还能继续匹配

 (只取红色部分)

我们就可以根据i的对称点j来更新答案,但是p[i]不一定等于p[j],因为mx后面的还是未知的,我们不知道,这一部分暴力匹配就是了

可知mx是一直在增大,每增大一次,我们需要比较一次,mx最多从0到n,所以也就是O(N)的复杂度

其实和KMP都有一个运用已知答案的思想

最后输出答案的时候我们半径减一即可(可以自己手%一下找找规律)

【模板】manacher算法

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 char s_new[21000002];
 4 char s[11000002];
 5 int p[21000002];
 6 int INIT(){//转换字符串 
 7     int len=strlen(s);
 8     s_new[0]='$';
 9     s_new[1]='#';
10     int j=2;
11     for(int i=0;i<len;i++)
12     {
13         s_new[j++]=s[i];
14         s_new[j++]='#';
15     }
16     s_new[j]='';
17     return j;
18 }
19 int manacher()
20 {
21     int len=INIT();
22     int ans=-1;
23     int id;//中心 
24     int mx=0;//能达到的已知的右端点 
25     for(int i=1;i<=len;i++)
26     {
27         if(i<mx)
28             p[i]=min(p[2*id-i],mx-i);//更新取较小的一个 
29         while(s_new[i+p[i]]==s_new[i-p[i]])//暴力扩展 
30             p[i]++;
31         if(mx<i+p[i])//更新mx 
32         {
33             id=i,
34             mx=p[i]+i;
35         }
36         ans=max(ans,p[i]-1);//更新答案 
37 
38     }
39     return ans;
40 }
41 int main()
42 {
43     scanf("%s",s);
44     cout<<manacher();
45     return 0;
46 }
原文地址:https://www.cnblogs.com/hualian/p/13801067.html