2015 Multi-University Training Contest 7 1003题
当时想到了用Manacher和二分,但是没想到用set去维护,可惜。
题解:
先用求回文串的Manacher算法,求出以第i个点和第i+1个点为中心的回文串长度,记录到数组c中比如10 9 8 8 9 10 10 9 8我们通过运行Manacher求出第i个点和第i+1个点为中心的回文串长度0 0 6 0 0 6 0 0 0
两个8为中心,10 9 8 8 9 10是个回文串,长度是6。两个10为中心,8 9 10 10 9 8是个回文串,长度是6。
要满足题目所要求的内容,需要使得两个相邻的回文串,共享中间的一部分,比如上边的两个字符串,共享 8 9 10这一部分。也就是说,左边的回文串长度的一半,要大于等于共享部分的长度,右边回文串也是一样。因为我们已经记录下来以第i个点和第i+1个点为中心的回文串长度,那么问题可以转化成,相距x的两个数a[i],a[i+x],满足a[i]/2>=x 并且 a[i+x]/2>=x,要求x尽量大
这可以用一个set维护,一开始集合为空,依次取出a数组中最大的元素,将其下标放入set中,每取出一个元素,再该集合中二分查找比i+a[i]/2小,但最大的元素,更新答案。然后查找集合中比i-a[i]/2大,但最小的元素,更新答案。
答案就是3*ans
自己的代码:
#include <set>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXN=200010;
set<int> st;
set<int>::iterator it;
int s [MAXN];
int Ma[MAXN];
int Mp[MAXN];
struct Node
{
int id;
int num;
}c[MAXN];
bool cmp(const Node x,const Node y)
{
return x.num>y.num;
}
void Manacher(int s[], int len)
{
int l=0;
Ma[l++]=-3;
Ma[l++]=-1;
for(int i=0; i<len; i++)
{
Ma[l++]=s[i];
Ma[l++]=-1;
}
Ma[l]=0;
int mx=0,id=0;
for(int i=0; i<l; i++)
{
Mp[i]=mx>i?min(Mp[2*id-i],mx-i):1;
while(Ma[i+Mp[i]]==Ma[i-Mp[i]])
Mp[i]++;
if(i+Mp[i]>mx)
{
mx=i+Mp[i];
id=i;
}
}
}
void work()
{
int i,j,n,num,id,ans;
scanf("%d",&n);
for(i=0;i<n;i++)
scanf("%d",&s[i]);
Manacher(s,n);
for(i=0;i<n;i++)
{
c[i].id=i;
c[i].num=Mp[2*i+3]/2;
}
/*
//for test
for(i=0;i<n;i++)
printf("%d",s[i]);
printf("\n");
for(i=0;i<n;i++)
printf("%d",c[i].num);
printf("\n");
*/
ans=0;
sort(c,c+n,cmp);
st.clear();
for(i=0;i<n;i++)
{
num=c[i].num;
id=c[i].id;
if(num==0) break;
st.insert(id);
it=st.upper_bound(num+id);
it--;
ans=max(ans,*it-id);
it=st.lower_bound(id-num);
ans=max(ans,id-*it);
}
ans*=3;
printf("%d\n",ans);
}
int main()
{
int i,T;
scanf("%d",&T);
for(i=1;i<=T;i++)
{
printf("Case #%d: ",i);
work();
}
return 0;
}