Manacher算法

本文详细介绍了Manacher算法,一种用于查找字符串最长回文子串的线性复杂度算法。通过构建特殊数组和计算len数组,动态维护最长回文串的信息。同时,展示了如何利用该算法解决实际问题,如计算序列中回文子串的数量,并给出了完整的C++代码实现。
摘要由CSDN通过智能技术生成

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<mx时:len[i]=min(mx-i,a[2*po-i])否则len[i]=1;再算len应该是多少:当以i为中心以len往外的字符串相等时我们就len[i]++;当更新完毕之后如果i+len[i]>mx,我们就要更新一下mx和po:mx=[+len[i],po=i;

代码如下:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
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){
			len[i]=min(mx-i,len[2*po-i]);
		}else len[i]=1;
		while(a[i+len[i]]==a[i-len[i]])len[i]++;
		if(i+len[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<iostream>
#include<algorithm>
#include<cstring>
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){
			len[i]=min(mx-i,len[2*po-i]);//
		}else len[i]=1;
		while(a[i-len[i]]==a[i+len[i]])len[i]++;
		b[i-len[i]+1]++;
		b[i+1]--;
		if(i+len[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;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值