BZOJ 2565: 最长双回文串 manacher

最长双回文串

题目描述

顺序和逆序读起来完全一样的串叫做回文串。比如acbca是回文串,而abc不是(abc的顺序为“abc”,逆序为“cba”,不相同)。输入长度为n的串S,求S的最长双回文子串T,即可将T分为两部分XY,(|X|,|Y|≥1)且XY都是回文串。

输入

一行由小写英文字母组成的字符串S

输出

一行一个整数,表示最长双回文子串的长度。

样例输入

baacaabbacabb

样例输出

12

提示

样例说明
从第二个字符开始的字符串aacaabbacabb可分为aacaa与bbacabb两部分,且两者都是回文串。
对于100%的数据,2≤|S|≤10^5


这个题显然是manacher

low版做法:左右各做一遍,最后扫一遍(是不是看不懂,我也看不懂)

高级版:扫一遍manacher,中间增加维护以一个字符为末尾的最长回文串

需要用到的性质如下:

Len数组简介与性质

Manacher算法用一个辅助数组Len[i]表示以字符T[i]为中心的最长回文半径字串的最右字符到T[i]的长度,比如以T[i]为中心的最长回文字串是T[l,r],那么Len[i]=r-i+1。 
对于上面的例子,可以得出Len[i]数组为: 
BZOJ 2565: 最长双回文串 manacher_第1张图片 
Len数组有一个性质,那就是Len[i]-1就是该回文子串在原字符串S中的长度

证明:

1、显然L=2∗Len[i]−1 即为新串中以Str[i]为中心最长回文串长度。 

2、以Str[i]为中心的回文串一定是以#开头和结尾的,例如“#b#b#”或“#b#a#b#”所以L 减去最前或者最后的‘#’字符就是原串中长度 的二倍,即原串长度为(L-1)/2,化简的Len[i]-1。得证。 依次从前往后求Len 数组就可以了,这里用到了DP(动态规划)的思想, 也就是求P[i] 的时候,前面的Len[]值已经得到了,我们利用回文串的特殊性质可以进行一个大大的优化。

觉得传送门讲的还不错可以去看看


#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

using namespace std;

typedef long long ll;

inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch<='9'&&ch>='0'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
	return x*f;
}
void print(int x)
{if(x<0)putchar('-'),x=-x;if(x>=10)print(x/10);putchar(x%10+'0');}

const int N=300100;

char s[N],ch[N];
int p[N],f[N];

int main()
{
	scanf("%s",ch+1);
	register int i,j,maxn,d,ans,n;
	n=strlen(ch+1);
	for(i=1;i<=n;++i)s[i<<1]=ch[i],s[(i<<1)-1]='#';s[(n<<1)+1]='#';s[0]='$';
	n<<=1;d=maxn=ans=0;
	for(i=1;i<=n;++i)
	{
		if(imaxn)
		{
			for(j=maxn+1;j<=i+p[i]-1;++j)f[j]=j-i+1;
			maxn=i+p[i]-1;d=i;
		}
		ans=max(ans,f[i-p[i]]+p[i]-1);
	}
	print(ans);puts("");return 0;
}
/*
baacaabbacabb

12
*/


你可能感兴趣的:(manacher,—————————字符串)