今天学长讲了讲马拉车算法,开始听的一脸懵,然后等结束了自己拿着草纸画了画,又看了看代码,开始逐渐明白马拉车算法的奥义
首先是将该字符串增加n+1个#,这样无论原字符串中元素个数是奇还是偶,最后的字符串中的元素都是奇数个
int n=strlen(a);
int T=2;
manacher[0]='$';//初始化边界防止越界
manacher[1]='#';
for(int i=0;i<n;i++){
manacher[T++]='#';
manacher[T++]=a[i];
}
manacher[T++]='0'; //初始化边界防止越界
接下来就是马拉车的核心代码了
int maxid=0; //用来记录最大的遍历位置
int id=0; //用于记录对称轴
for(int i=0;i<k;i++){
if(maxid>i){ //如果最大的遍历位置的点大于当前要求的点的位置
p[i]=min(maxid-i,p[2*id-i]); //当前遍历过的位置和它对面的对称坐标取最小值;因为之后还要对p[i]进行加的操作,而且p[i]的初始值不能超过maxid-i(因为当前搜索到的最大值-i就等于当前已经知道的p[i]的最大可能情况)
}else{//否则的话从1开始
p[i]=1;
}
while(manacher[i-p[i]]==manacher[i+p[i]])p[i]++;//如果满足回文拓展指针大小
if(i+p[i]>maxid){//如果是最大,那就记录最大
maxid=i+p[i];
id=i;
}
}
manacher 扩展序列与原序列的关系
(1):新串中的以位置i为中心的回文串 i ,对应原始串的( (i-len[i])/2 , (i+len[i])/2-2 )
(2):以i为中心的最长回文的长度为p[i]-1;
容易证明,manner中的回文串中#会比字母多一,而且若算上#,回文串的长度为2*p[i]-1,所以可以得出字母的长度为p[i]-1;
接下来来看看板子题吧:
回文串
没啥好说的,标标准准的板子题;
#include<iostream>
#include<string.h>
#include<algorithm>
#include<stdio.h>
#include<cmath>
#include<queue>
#include<stack>
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
const int N=1400;
char a[N];
char manacher[N<<1];
int p[N<<1];
int main()
{
while(scanf("%s",a)!=EOF){
memset(p,0,sizeof p);
memset(manacher,0,sizeof manacher);
int T=2;
manacher[0]='$';
manacher[1]='#';
int n=strlen(a);
for(int i=0;i<n;i++){
manacher[T++]='#';
manacher[T++]=a[i];
}
manacher[T++]='0'; //初始化数组
int maxid=0;
int id=0;
int ans=0;
for(int i=0;i<T;i++){
if(maxid>i){
p[i]=min(maxid-i,p[id*2-i]);
}
else{
p[i]=1;
}
while(manacher[i-p[i]]==manacher[i+p[i]])
{
if(i+p[i]>=T){ //判断不超过字符串
break;
}
p[i]++;
}
if(i+p[i]>maxid){
maxid=i+p[i];
id=i;
}
ans=max(ans,p[i]-1);
}
cout<<ans<<"\n";
}
}
来看看稍微扩展一点
马拉车简单扩展
这道题其实也算一个板子题,但是稍微不一样的就是加了一个判断,就是从左到中间的序列是非递减的
这时候其实只需要将‘#’换成小于50的数即可,因为要满足从左到中间的序列是非递减的,同时加入一个judge记录上一个人的身高就可以了,若是manacher[i]<=50,此时只需要把judge换成任意一个大于250的数就可以了
#include<iostream>
#include<string.h>
#include<algorithm>
#include<stdio.h>
#include<cmath>
#include<queue>
#include<stack>
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
const int N=1e5+10;
int a[N];
int manacher[N<<1];
int q[N<<1];
int main()
{
int t;
scanf("%d",&t);
while(t--){
memset(q,0,sizeof(q));
int n;
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
}
manacher[0]=-1;
manacher[1]=10;
int k=2;
for(int i=0;i<n;i++){
manacher[k++]=a[i];
manacher[k++]=10;
}
manacher[k++]=-2;
int ans=0;
int maxid=0;
int id=0;
int judge=0;
for(int i=0;i<k;i++){
if(maxid>i){
q[i]=min(maxid-i,q[id*2-i]);
}
else{
q[i]=1;
}
if(manacher[i]==10){
judge=260;
}
else{
judge=manacher[i];
}
while(manacher[i-q[i]]==manacher[i+q[i]]&&manacher[i-q[i]]<=judge){
if(manacher[i-q[i]]!=10){
judge=manacher[i-q[i]];
}
q[i]++;
}
if(i+q[i]>maxid){
maxid=i+q[i];
id=i;
}
ans=max(ans,q[i]-1);
}
cout<<ans<<"\n";
}
}