manacher算法
用来找字符串中的最长回文子串的algorihtm
Manacher算法可以将长度为奇数和偶数的回文串一起考虑:
在原字符串的相邻字符串之间插入一个分隔符,字符串的首尾也要分别添加,
注意分隔符必须是原字符串中没有出现过的
原字符串s a b a b c
转换后字符串str # a # b # a # b # c #
一、Len数组的简单介绍
Manacher算法中用到一个非常重要的辅助数组
Len[i]表示以str[i]为中心的最长回文子串的最右端到str[i]位置的长度,
比如以str[i]为中心的最长回文串是str[l,r],那么Len[i]=r-i+1
转换后的字符串str # a # b # a # b # c #
Len 1 2 1 4 1 4 1 2 1 2 1
Len[i]数组有一个性质,Len[i]-1就等于该回文串在原串s中的长度
证明:在转换后的字符串str中,所有的回文串的长度都是奇数,
那么对于以str[i]为中心的最长回文串的长度为2*Len[i]-1,
其中又有Len[i]个分隔符,所以在原字符串中的长度就是Len[i]-1,
那么剩下的工作就是求Len数组
二、Len数组的计算
从左往右开始计算,假设0<=j<=i,那么在计算Len[i]时,Len[j]已经计算过了,
设mx为之前计算过的最长回文串的右端点,
id为取得这个端点值得位置(那么Len[id]=mx-id+1)
第一种情况:i < mx
找到i相对于id的对称位置,设为j,再次分为两种情况:
1、Len[j] < mx-i
mx的对称点为2*id-mx,i和j所包含的范围是2*Len[j]-1
那么说明以j为中心的回文串一定在以id为中心的回文串内部,
且i和j关于id对称,由回文串的定义可知,一个回文串反过来仍是回文串,
所以以i为中心的回文串长度至少和以i为中心的回文串长度相等,
即Len[i]>=Len[j].因为Len[j]<mx-i所以i+Len[j]<mx,
由对称性可知Len[i]=Len[j].
2、Len[j] > mx-i(此处不能等)
由对称性说明以i为中心的回文串可能延伸到mx之外,
而大于mx的部分我们还没有进行匹配,
所以要从mx+1位置开始一个一个匹配直到失配,
从而更新mx和对应的id以及Len[i]
第二种情况,i>mx或Len[j]=mx-i
1.如果说等于的时候会可能Len[i]仍有可能会增大
2,如果i比mx还大,说明对于中点为i的回文串一点都没匹配,
这个两种情况只能一个个匹配,匹配完成后更新mx的位置和对应的id及Len[i].
具体实现见代码
#include<bits/stdc++.h>
#define N 500000
using namespace std;
char s[N*2],str[N*2];
int Len[N*2],len;
void getstr(){
int k=0;
str[k++]='$';
for(int i=0;i<len;i++)
str[k++]='#',
str[k++]=s[i];
str[k++]='#';
len=k;
}
void Manacher(){
getstr();
int mx=0,id;
for(int i=1;i<len;i++){
if(mx>i) Len[i]=min(Len[2*id-i],mx-i);
else Len[i]=1;
while(str[i+Len[i]]==str[i-Len[i]])
Len[i]++;
if(Len[i]+i>mx) mx=Len[i]+i,id=i;
}
}
int main(){
while(~scanf("%s",&s)){
len=strlen(s);
Manacher();
int ans=1;
for(int i=1;i<len;i++) ans=max(ans,Len[i]);
printf("%d\n",ans-1);
}
return 0;
}
讲完了模板现在分析一下这道题目,这题是一题模板题;
首先读入有点麻烦为此wa了一次,读入要注意细节,
第二就是用到了那个映射,因为原串和后来的串是有区别的;
详见代码
include<bits/stdc++.h>
#define N 200005
using namespace std;
char s[N*2],str[N*2],ss[N*2];
int Len[N*2],len,n,idd,mp[N*2];
void getstr(){
int k=0;
str[k++]='$';
for(int i=0;i<len;i++){
str[k++]='#';
str[k++]=ss[i];
}
str[k++]='#';
len=k;
}
void Manacher(){
getstr();
int mx=0,id;
for(int i=1;i<len;i++){
if(mx>i) Len[i]=min(Len[2*id-i],mx-i);
else Len[i]=1;
while(str[i+Len[i]]==str[i-Len[i]])
Len[i]++;
if(Len[i]+i>mx) mx=Len[i]+i,id=i;
}
}
void read(){
char c;
while((c=getchar())!=EOF){
s[++n]=c;
if ((c>='A'&&c<='Z')||(c>='a'&&c<='z')){
if(c>='A'&&c<='Z') ss[len++]=c+32;
if(c>='a'&&c<='z') ss[len++]=c;
mp[len]=n;
}
}
}
int main(){
read();
Manacher();
int ans=1;
for(int i=1;i<len;i++){
if(ans<Len[i]){
ans=Len[i];
idd=i;
}
}
ans--;
printf("%d\n",ans);
int sx=(idd-ans)/2;
int sy=(idd+ans)/2;
for(int i=mp[sx+1];i<=mp[sy];i++)
printf("%c",s[i]);
return 0;
}