最长回文子串问题(马拉车问题),小白月赛B

一、最长回文子串问题

用O(n)的时间求出最长的回文子串,过程类似kmp算法的pre数组的预处理。

 

流程:

(1)给出一个字符串,eg:s1 = "abababa",

然后将s2 = “% # a # b # a # b # a    #   b   #   a  #”,

     下标 :    0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

是对字符串的预处理,%的作用是防止超限,因为跑到最后一个字符‘#’相同,肯定会行前

在找一位,就到-1了,就越界了。

#的作用类似于观众,就是强行将字符串的字符数量凑为偶数(在不算%符号的情况下)。

(2)对字符串正式的处理前,先理解几个变量

id:表示上一个的最长的回文串的中点;

eg:s2串,从下表0开始,当处理到i=6的情况时,最长的子串的中点是id = 4。

mx:表示上一个最长回文串所能达到的点:

eg:还是i=6的情况,上一个最长子串的中点是id = 4,最长可以处理到mx = 7的情况。

len[i]数组,就是i所能达到的最长的数组的最长半径,就是回文串的最长半径。

ans:就是最长回文子串的长度,因为ans = len[i]-1就是最终回文子串的长度。

ans对应的代码(注意:代码中的长度是回文子串长度+1):

if(len[i]+i>mx){
				mx = len[i]+i;
				id = i;
				ans = MAX(ans,len[i]);
			}

 

(3)求解回文子串的长度:

优化:

首先,如果i在mx(就是i在上一个回文子串所能达到的最长长度的范围内),考虑已知区间[id-len[id] , id+len[id] ]范围内

已知这个区间是一个回文串,所以可以利用回文串的对称性,求解len[i]就是关于id的对称坐标len[id*2-i]的长度(这里神似kmp的前后缀匹配的j = pre[j],就是利用已知条件优化,更快的求出未知的。)

这是一般情况(其中j=id*2-i,i就是现在的坐标,my是id的最左边,mx是id的最右边):

但是,还要考虑到一种特殊情况,就是:

这样就是j = id*2-i这个点的回文串是超出了id所能达到的最大范围,用图解释就是j超出my的部分不算。

所以由以上一大段话可知:

if(i

其次,如果i不在mx的范围内,就将长度置为1,反正要有‘#’作群众演员,而且最终结果还是len[i]-1,所以肯定不能是0

(这里有木有神似kmp的一开始就置一这种情况)

所以代码是:

//承接上面的if
else len[i] = 1;

到此为止,最长回文子串的核心代码都介绍完了,

奉上我的参考文章o(* ̄︶ ̄*)o,特此声明,感谢chl和我一起探讨参考文章,还有以上的图片均是来源于参考文章。

 

然后以hihocoder的一道题为例:

参考代码:

#include
#include
#include
using namespace std;
const int maxn = 3e6+10;
int len[maxn];
char s1[maxn],s2[maxn];
int MIN(int x,int y){
	return xy?x:y;
}
int main(void)
{
	int T,n,i,j;
	scanf("%d",&T);
	while(T--){
		scanf("%s",s1);
		int ls = strlen(s1);
		s2[0] = '$';
		for(i=0,j=1;imx){
				mx = len[i]+i;
				id = i;
				ans = MAX(ans,len[i]);
			}
		}
		printf("%d\n",ans-1);
	}
	return 0;
}

 

 

小白月赛B题:

 

思路:

将子串变为两倍的子串,就是将s1 = “abc”变为 s2 = "abcabc",然后查找i到i+len范围内的最回文长子串。

 

代码:

#include
#include
#include
#include
using namespace std;
string s1;
int cc[100100];
int MAX(int x,int y){
	return x>y?x:y;
}
int MIN(int x,int y){
	return xmx){
			mx = cc[i]+i;
			id = i;
			ans = MAX(ans,cc[i]);
		}
	}
	return ans-1;
}
int main(void)
{
	cin>>s1;
	int l1 = s1.length(),len = l1*2,ans = 0;
	s1 = s1+s1;
	for(int i=0;i

 

 

你可能感兴趣的:(牛客网)