ACM入门之manacher(马拉车)算法
Posted 辉小歌
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ACM入门之manacher(马拉车)算法相关的知识,希望对你有一定的参考价值。
本文参考了:FREEH在https://www.luogu.com.cn/problem/solution/P3805中的题解。
主要用途:O(n)的时间内求一个字符串的最大回文长度。或者 求其所有子串中回文串的个数。
算法过程:
- 预处理。
由于回文串分为偶回文串和奇回文串,奇偶判断起来比较麻烦,因此我们可以在字符串的首、尾以及各个字符之间添加一些“神奇”字符(不 妨使用$),但是要注意字符串的首添加的字符必须区别于各个字符之间的字符。不难发现,修改后的字符串都变成了奇字符串。 - 几个变量的含义
p[i]
表示以字符i
为回文中心的最长回文串的半径,不难发现,p[i]-1
就是字符串中最长回文串的长度(因为要去除$)。mx:
表示目前找到的回文串的右端的最右是mx
。mid
:表示目前找到的回文串的中心是mid
。
例图说明:
判断字符串的最长回文串的长度的板子。
const int N=1e7*3+10;
int n,p[N];
char a[N],b[N];
int init()
n=strlen(a);
int k = 0;
b[k ++ ] = '^', b[k ++ ] = '#';
for (int i = 0; i < n; i ++ ) b[k ++ ] = a[i], b[k ++ ] = '#';
b[k]='\\0';
return k;
void manacher()
scanf("%s",a);
int mr = 0, mid;
int max_len=0;
n=init();
for (int i = 1; i < n; i ++ )
if (i < mr) p[i] = min(p[mid * 2 - i], mr - i);
else p[i] = 1;
while (b[i - p[i]] == b[i + p[i]]) p[i] ++ ;
if (i + p[i] > mr)
mr = i + p[i];
mid = i;
max_len = max(max_len, p[i] - 1);
cout<<max_len;
判断数字数组的最长回文串的长度的板子。
const int N=1e6*2+10;
int n,p[N],a[N],b[N];
int init()
int k = 0;
b[k ++ ] = 1e9+1, b[k ++ ] = 1e9+2;
for (int i = 0; i < n; i ++ ) b[k ++ ] = a[i], b[k ++ ] = 1e9+2;
return k;
void manacher()
cin>>n;
for(int i=0;i<n;i++) cin>>a[i];
int mr = 0, mid;
int max_len=0;
n=init();
for (int i = 1; i < n; i ++ )
if (i < mr) p[i] = min(p[mid * 2 - i], mr - i);
else p[i] = 1;
while (b[i - p[i]] == b[i + p[i]]) p[i] ++ ;
if (i + p[i] > mr)
mr = i + p[i];
mid = i;
max_len = max(max_len, p[i] - 1);
cout<<max_len<<endl;
例题一:
https://www.luogu.com.cn/problem/P3805
#include<bits/stdc++.h>
using namespace std;
const int N=1e7*3+10;
int n,p[N];
char a[N],b[N];
int init()
n=strlen(a);
int k = 0;
b[k ++ ] = '^', b[k ++ ] = '#';
for (int i = 0; i < n; i ++ ) b[k ++ ] = a[i], b[k ++ ] = '#';
b[k]='\\0';
return k;
void manacher()
scanf("%s",a);
int mr = 0, mid;
int max_len=0;
n=init();
for (int i = 1; i < n; i ++ )
if (i < mr) p[i] = min(p[mid * 2 - i], mr - i);
else p[i] = 1;
while (b[i - p[i]] == b[i + p[i]]) p[i] ++ ;
if (i + p[i] > mr)
mr = i + p[i];
mid = i;
max_len = max(max_len, p[i] - 1);
cout<<max_len;
int main(void)
manacher();
return 0;
例题二:
p[i] 表示以字符i为回文中心的最长回文串的半径
故 [i-p[i]+1,i]
区间内和其以i为对称轴对称的都是回文串。区间加用差分。
#include<bits/stdc++.h>
using namespace std;
const int N=1e6*2+10;
int n,p[N];
int a[N],b[N],d[N];
int init()
int k = 0;
b[k ++ ] = 1e9+1, b[k ++ ] = 1e9+2;
for (int i = 0; i < n; i ++ ) b[k ++ ] = a[i], b[k ++ ] = 1e9+2;
return k;
void manacher()
int mr = 0, mid;
int max_len=0;
n=init();
for (int i = 1; i < n; i ++ )
if (i < mr) p[i] = min(p[mid * 2 - i], mr - i);
else p[i] = 1;
while (b[i - p[i]] == b[i + p[i]]) p[i] ++ ;
if (i + p[i] > mr)
mr = i + p[i];
mid = i;
max_len = max(max_len, p[i] - 1);
void add(int l,int r,int c)
d[l]+=c;
d[r+1]-=c;
int main(void)
cin>>n;
for(int i=0;i<n;i++) cin>>a[i];
manacher();
for(int i=1;i<n;i++)
int l=i-p[i]+1,r=i;
add(l,r,1);
for(int i=1;i<n;i++) d[i]+=d[i-1];
for(int i=1;i<n;i++) if(i%2==0) cout<<d[i]<<" ";
return 0;
以上是关于ACM入门之manacher(马拉车)算法的主要内容,如果未能解决你的问题,请参考以下文章