数据结构可视化网址

2021年山东省安全员C证考试题库及山东省安全员C证考试资料

  返回  

Manacher(马拉车)

2021/8/21 20:16:18 浏览:

Manacher

一 、背景

1975年,Manacher发明了Manacher算法(中文名:马拉车算法),是一个可以在O(n)的复杂度中返回字符串s中最长回文子串长度的算法,十分巧妙。

让我们举个例子:
1.字符串:abbababa 最长回文子串:5(abbababa

2.字符串:abcbbabbc 最长回文子串:7(abcbbabbc

3.字符串:abccbaba 最长回文子串:6(abccbaba)

传统方法是,遍历每个字符,以该字符为中心向两边查找。时间复杂度为O(n^2),效率很差;
但是Manacher算法的时间复杂度可以达到O(n)!
在这里插入图片描述
下面让我们看看它是怎么做的的吧在这里插入图片描述

二、算法过程分析

回文分为奇回文(ababa)和偶回文(abba),这里比较难以处理,我们使用一个骚操作(划重点)。

我们将字符串首尾和每个字符间插入一个字符(注意:这个自符在串中并未出现)例如:’#’ s='abbadcacda’先转化成s_new=$#a#b#b#a#d#c#a#c#d#a#\0’(加粗的是边界)

这样原串中的偶回文(abba)与奇回文(adcacda),变成了(#a#d#d#a#)与(#a#d#c#a#c#d#a#)两个奇回文

定义数组p,用p[i]表示以i为中心的最长回文半径。再次举个例子
在这里插入图片描述
(图片是借鉴了他人的)

在这里插入图片描述

定义两个变量mx和id。mx就是以id为中心的最长回文右边界,也就是mx=id+p[id],随后我们需要mx做出它的最大贡献。

假设我们在求p[i](以i为中心的最长回文半径),如果i<mx(如上图),那么我们就用mx和j来更新到我们已知的可以更新的最大长度,代码如下:

if(i<mx)  
    p[i]=min(p[2*id-i],mx-i);

2*id-i是i关于id的对称点(上图j)(证明:i-id=id-j),而p[j]表示以j为中心的最长回文半径,这样我们就可以利用p[j]和mx加快速度了。

为什么要用p[j]和mx-i取min来更新呢?
首先我们想一下,p[j](以j为中心的最长回文半径)是已经知道了(因为是从前面扫过来的),若是p[j]>mx-i,我们是可以知道以j为中心,以mx的对称点到j的距离为半径形成的回文字符串是肯定存在的,并且id的左边直到mx的对称点与id的右边直到mx是对应的,所以mx是i目前可以更新到的最大回文半径;(还不明白的话就停下来画图好好想想)
若p[j]<mx-i,证明j的回文半径不到mx的对称点到j的距离,再次通过(id的左边直到mx的对称点与id的右边 直到mx是对应的),所以p[i]=p[j]。
但是取完min并不是最大的回文半径,接下来的就暴力搜就好了

代码

#include<bits/stdc++.h>
using namespace std;
char s[1100000];
char sn[1100000];
int a[1100000];
int ycl()
{
	int len=strlen(s);
	sn[0]='$';
	sn[1]='#';
	int sum=2;
	for(int i=0;i<=len;i++)
	{
		sn[sum++]=s[i];
		sn[sum++]='#';
	}
	sn[sum]='\0';
	return sum;
}
int mlc()
{
	int cd=ycl();
	int mx=0,maxlen=-1,id;
	for(int i=1;i<=cd;i++)
	{
		if(i<mx)a[i]=min(a[id*2-i],mx-i);
		else a[i]=1;
		while(sn[i-a[i]]==sn[a[i]+i])a[i]++;
		if(mx<a[i]+i)
		{
			id=i;
			mx=a[i]+i;
		}
		maxlen=max(maxlen,a[i]-1);
	}
	return maxlen;
}
int main()
{
	scanf("%s",s);
	printf("%d",mlc());
	return 0;
}

联系我们

如果您对我们的服务有兴趣,请及时和我们联系!

服务热线:18288888888
座机:18288888888
传真:
邮箱:888888@qq.com
地址:郑州市文化路红专路93号