题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5371
题目大意:给一串数字,在子串中找到“1-2-1”的形式,其中1和2 是回文串,找出最长的那一串。
思路:利用manacher算法得出最长序列。观察子串形式,1和2是回文串,其实2和后面那个1也是回文串。
在之前我们已经通过manacher算法得到了每个数字所能延伸的长度,所以我们只要枚举第二段到第三段的距离即可。
当遇到第i个数字时,那么第二段和第三段的距离就是i+p[i],令j=i+p[i]-1,那么对于j这个位置就有p[j],我们只要j-(p[j]-1) <= i 就可以了。
这里的意思就是:j的回文半径大于i的回文半径,这样就能保证第二段和第三段也是回文串了。
注意有个优化:如果j-i<max,那么久不需要去看了。
#include<stdio.h>
#include<string.h>
#define min(a,b) a<b?a:b
int str[220005],s[220005];
int len,p[220005],a[22005];
void solve()
{
int i,mx,id;
mx=0;
for(i=1;i<len;i++)
{
if(mx>i){
p[i]=min(p[2*id-i],p[id]+id-i);
}
else p[i]=1;
for(;str[p[i]+i]==str[i-p[i]];p[i]++)
;
if(p[i]+i>mx){
mx=p[i]+i;
id=i;
}
}
}
void init()
{
str[0]=-1;str[1]=-2;
for(int i=0;i<len;i++)
{
str[i*2+2]=a[i];
str[i*2+3]=-2;
}
len=len*2+2;
str[len]=-2;
}
int main()
{
int i,j,k,T,t=0;
scanf("%d",&T);
while(T--)
{
t++;
scanf("%d",&len);
memset(p,0,sizeof(p));
for(i=0;i<len;i++)
{scanf("%d",&a[i]);
// s[i]=a[i]+'0';
}
init();
solve();
int ans=0;
int maxi=0;
int f=0;
for(i=1;i<len;i+=2)
{
for(j=i+p[i]-1;j-i>maxi;j-=2) //只要看-2的位置即可。
{
if(j-p[j]+1<=i){
maxi=j-i;
break;
}
}
}
maxi=maxi/2*3;
printf("Case #%d: ",t);
printf("%d\n",maxi);
}
return 0;
}
/*
14
1 2 2 1 1 2 3 3 2 1 1 2 3 4
*/
/*
100
15
1 2 3 4 5 5 4 3 2 1 1 2 3 4 5
15
3 4 5 5 4 3 2 1 1 2 3 4 5 5 4
10
2 3 4 4 3 2 2 3 4 4
11
1 1 2 3 3 2 1 1 2 3 3
*/