最长双回文串
题目描述
顺序和逆序读起来完全一样的串叫做回文串。比如acbca是回文串,而abc不是(abc的顺序为“abc”,逆序为“cba”,不相同)。输入长度为n的串S,求S的最长双回文子串T,即可将T分为两部分X,Y,(|X|,|Y|≥1)且X和Y都是回文串。
输入
一行由小写英文字母组成的字符串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]数组为:
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<cmath>
#include<ctime>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<complex>
#include<iostream>
#include<algorithm>
#include<iomanip>
#include<vector>
#include<string>
#include<bitset>
#include<queue>
#include<map>
#include<set>
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(i<maxn)p[i]=min(p[(d<<1)-i],maxn-i);else p[i]=1;
while(!(s[i-p[i]]^s[i+p[i]]))p[i]++;
if(i+p[i]-1>maxn)
{
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
*/