Manacher算法是查找一个字符串的最长回文子串的线性算法
设原数组为s,先构造一个a数组:把特殊的字符插进s的每个字符之间,注意下标从1开始
例如s【1-5】:1 1 1 1 1
a【1-10】:-1 1 -1 1 -1 1 -1 1 -1 1
然而当我们防止边界问题往往会把a【0】设置成一个不同于插入字符的字符,这个可以是-2;
那么a【0-10】:-2 -1 1 -1 1 -1 1 -1 1 -1 1
然后我们再构造一个len数组来表示以i为中心的最大回文串的最右端的字符到i之间(包括i和这个数)的最大长度是多少。先设一个mx=0来表示最右边的数的坐标,po表示以po为中心的点的坐标。计算方法:当i
代码如下:
#include#include #include #include using namespace std; const int N=3e6+10; int a[N],len[N]; int n,x; int main(){ cin>>n; a[0]=0; for(int i=1;i<=n;i++){//构造数组 cin>>x; a[i*2-1]=-1; a[i*2]=x; } a[2*n+1]=0; int mx=0,po=0; for(int i=1;i<=2*n;i++){//算len数组 if(i mx){ mx=i+len[i]; po=i; } } return 0; }
如果想求每个数为头的字符串的数量,要用b数组来存,每次更新完len时b[i-len[i]+1]++,b[i+1]--
最后对b数组求前缀和当下标为偶数时输出就行。
例题:花非花(第十八届西南科技大学ACM程序设计竞赛(同步赛))
链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
给出一个序列 ana_nan,对于 iii,求有几个数 jjj 满足:
1 、i≤j
2 、子串 ai∼j 是一个回文串
#include#include #include using namespace std; const int N=3e6+10; int a[N],b[N],len[N]; int n,x; int main(){ cin>>n; a[0]=0; for(int i=1;i<=n;i++){//构造一个数组 cin>>x; a[i*2-1]=-1; a[i*2]=x; } a[n*2+1]=0; int mx=0,po=0;//mx是上次更新时最长子串最右边的数的坐标,po是中间对称的数的坐标 for(int i=1;i<=2*n;i++){ if(i mx){ mx=i+len[i]; po=i; } } for(int i=1;i<=2*n;i++){ b[i]+=b[i-1]; if(i%2==0)printf("%d ",b[i]); } return 0; }



