題意:給你一串數字,要你找到這裏面最大的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);
}
}