manacher(马拉车)算法求最长回文子串

问题提出

:给定一个字符串,要求其最长回文子串
例如
“aaaa”,其最长回文子串为4,即“aaaa”,
“abcd”,最长回文子串为1,
“abccb”,最长回文子串为4,即“bccb”。

如果直接对每一个字符遍历,分别向两边查找的话,时间复杂度为O( n 2 n^2 n2),效率很低。
那有没有更高效的算法呢。
1975年,一个叫Manacher的人发明了一种算法,即manacher算法,时间复杂度可以达到O( n n n)。

算法描述

1 、 1、 1预处理

由于回文串分为奇回文(长度为奇数)、和偶回文(长度为偶数),本因分两种情况处理,但为了简化这一步,我们将对原字符串进行处理,使之变成奇回文。
具体做法是在每个字符左右两边都加上一个特殊字符,如:
原字符串为,abba,长度为4,
处理后,#a#b#b#a#,长度为9。
原字符串为,aba,长度为3,
处理后,#a#b#a#,长度为7。

2 、 2、 2最长回文子串长度

我们引入一个辅助数组,int p[ ]p[i]表示以原字符串s[]i位置处s[i]为中心的最长回文半径
以字符串 abbaf 为例,预处理后字符串为 #a#b#b#a#f#

i       0  1  2  3  4  5  6  7  8  9 10  11 12
arr[i]  #  c  #  a  #  b  #  b  #  a  #  f  #
p[i]    1  2  1  2  1  2  5  2  1  2  1  2  1

在上面这个例子中,最长回文子串为abba。以s[6]为中心,其最长回文半径为p[6]=5,所覆盖的字符串是#a#b#b#a#,对应的原始字符串为abba,即最长回文子串,长度为4,可以通过5-1得到。
接下来我们来探究最长回文子串长度最长回文半径之间的关系
再看几个例子,aba, 转换之后为#a#b#a#,最长回文子串为aba,长度为3,最长回文半径为p[4]=4
再如,#a#e#f#e#a#,最长回文子串为aefea,长度为5,计算得最长回文半径为p[6]=6
我们发现,最长回文子串长度=最长回文半径-1,即p[i]-1
事实上,的确存在这个结论。我们可以这样理解,最长回文子串即以某一点i为中心,以p[i]为半径所覆盖的线段,那么对于原始字符串最长回文子串长度=p[i]*2。由于预处理后,每个字符左右都加了一个特殊字符,所以最长回文半径变为了原来的2倍,最长回文子串长度变为了原来的*2-1,那么最长回文子串长度=p[i]-1

3 、 3、 3计算p[]数组

现在知道了要求的最长回文子串长度与最大回文半径p[i]有关,那么接下来就讨论最大回文半径p[i] 怎么求。
我们设置两个变量idmxid表示能延伸到最右端的回文子串的中心位置,mx表示该子串的最右端位置。
那么有,mx=id+p[id]
在这里插入图片描述
对于i<mx,在以id为中心的回文子串中,存在另一以j为中心的回文子串,与以i为中心的回文子串关于id对称,且有p[j]=p[i]。由于对称的性质,所以有i+j=id*2j=id*2-i
考虑到i的回文子串可能存在超出mx的部分,所以此时p[i]=mx-i,依然有p[j]=p[i]
至于超出mx的部分是否满足上述条件,就需要遍历去比较字符了。
所以,在i<mx的情况下,有p[i]=min(p[id*2-i],mx-i)
当满足mx<i+p[i]时,更新mx=i+p[i]id=i

4 、 4、 4参考代码 C++

char s[maxn];//原始字符串
char s1[maxn];//预处理后字符串
char p[maxn];//最长回文半径
int init()//预处理
{
	int len=strlen(s);
	s1[0]='$';//处理边界,防止越界
	s1[1]='#';
	int j=2;
	for(int i=0;i<len;i++)
	{
		s1[j++]=s[i];
		s1[j++]='#';
	}
	s1[j]='\0';//处理边界,防止越界
	
	return j;//返回处理后的字符串长度
}
int manacher()//计算最长回文子串长度
{
	int len=init();
	int maxlen=-1;//最长回文子串长度
	int id;//可以不初始化是因为初始循环一定会执行if(mx<i+p[i])
	int mx=0;
	for(int i=1;i<len;i++)
	{
		if(i<mx)
			p[i]=min(p[2*id-i],mx-i);
		else p[i]=1;
		while(s1[i-p[i]] == s1[i+p[i]])//遍历比较字符,因为已经处理过边界,所以无需进行边界判断
			p[i]++;
		if(mx<i+p[i])//更新id和mx
		{
			id=i;
			mx=i+p[i];
		}
		maxlen=max(maxlen,p[i]-1);
	}
	return maxlen;//返回最长回文子串长度
}

5 、 5、 5参考习题
洛谷P3805 【模板】manacher算法
or HDU3068 最长回文

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值