题意:给你一串数字,要你找到这里面最大的N-序列的长度。N序列满足下面两个条件:1、第一部分与第三部分相同; 2、第一部分与第二部分对称;例如:2,3,4,4,3,2,2,3,4 就是一个N-序列。通过观察,我们知道N序列包含两个回文序列,上面的例子中2,3,4,4,3,2和4,3,2,2,3,4 两个回文。由于题目中给的数据量比较大,所以暴力肯定超时。所以用到了处理回文序列的Manacher算法。该算法的核心是利用回文串左右对称的性质,例如有个回文串 1 2 1 3 4 3 1 2 1,可以看到,4的左边和右边对称,最大对称长度为4,。以4左边的2为中心的最大对称长度为1(1,2,1为回文序列,2为中心,对称长度为1),所以,以4右边的2为中心的最大对称长度也为1,这只是一种情况。当回文串为 1 2 1 3 4 3 1 2 1 3时,以右边的2为中心的最大对称长度就为2了,但我们可以确定的是,以右边的2为中心的最大对称长度一定不小于1,否则以4为中心的最大对称长度就不会是4了。这里给个关于此算法详细解释的链接http://blog.csdn.net/ggggiqnypgjg/article/details/6645824
理解之后就可以做题了。注意的是,用此方法求的只是最大对称长度,而可能N-序列的两个回文长度不是对称中心的最大对称长度,例如在2 2 3 4 4 3 2 2 3 4中,2 2 3 4 4 3 2 2是回文串,但它不是N-序列中的回文串,那两个回文串依旧是2 3 4 4 3 2和4 3 2 2 3 4。
代码如下
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <set>
using namespace std;
int n;
const int maxn=1e+5+5;
int a[2*maxn];
typedef struct
{
int p;
int num;
}h;
h pp[2*maxn];
bool cmp(const h &a,const h &b)
{
if(a.p>b.p)
return true;
if(a.p==b.p&&a.num<b.num)
return true;
return false;
}
void KP()
{
int mx=0;
int id;
for(int i=1;i<=2*n+1;i++)
{
pp[i].num=i;
if(mx>i)
pp[i].p=min(pp[2*id-i].p,mx-i);
else
pp[i].p=1;
while(a[pp[i].p+i]==a[i-pp[i].p])pp[i].p++;
if(pp[i].p+i>mx)
{
mx=pp[i].p+i;
id=i;
}
}
}
set<int>T;
set<int>::iterator it;
int max_s()
{
KP();//即Manacher算法
sort(pp+1,pp+2*n+2,cmp);
int M=0;
T.clear();//一定要注意初始化啊
for(int i=1;i<=2*n+1;i++)
{
int u=pp[i].num;
if(a[u]!=-1)continue;
if(pp[i].p==1) break;
T.insert(u);
it=T.lower_bound(u+pp[i].p);//也可以写成it=T.upper_bound(u+pp[i].p);
it--;
M=max(M,*it-u);
it=T.lower_bound(u-pp[i].p);
M=max(M,u-*it);
}
return (M/2)*3;
}
int main()
{
int cc;
scanf("%d",&cc);
int cnt=0;
while(cnt<cc)
{
cnt++;
scanf("%d",&n);
a[1]=-1;
a[0]=-2;
for(int i=2;i<=n*2;i+=2)
{
scanf("%d",&a[i]);
a[i+1]=-1;
}
a[n*2+2]=-3;
int ans=max_s();
printf("Case #%d: %d\n",cnt,ans);
}
}