/*像我这种菜鸡果然还是要写写博客,不然有的题目真是过去就过去了的状态。自己在硬盘里的代码也不太好管理,等以后写个软件来搞就好了 - - */
题意:
给出一个串,找出满足以下条件的最长子串:
1. the first part is the same as the thrid part,
第一个部分和第三个部分一样。
2. the first part and the second part are symmetrical.
第一个部分和第二个部分对称。
思路:
这道题其实就是马拉车算法的简单应用
从题意出发 我们发现其实他想说的是 :
(设把s的一个子串分成三个子串 第i个子串为si)
|s1|==|s2|==|s3|
s1+s2是回文串
s2+s3是回文串
所以我们用马拉车(就爱说这个 - -)可以知道其实就是求最长的一个子串满足:
设:s1和s2中间的分隔符为中心的回文串的长度p1
s2和s3中间的分隔符为中心的回文串的长度p2
min(p1,p2)-1>=|s2|
一开始是对于每个分隔符暴力找 若找到则记录长度
但是TLE了 - -
所以 对j的循环做了一些优化 若小于所记录的就跳出
代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cassert>
using namespace std;
const int MAXN = 1e6+10;
int p[MAXN];
//适用于正负整数
template <class T>
inline bool scan_d(T &ret) {
char c; int sgn;
if(c=getchar(),c==EOF) return 0; //EOF
while(c!='-'&&(c<'0'||c>'9')) c=getchar();
sgn=(c=='-')?-1:1;
ret=(c=='-')?0:(c-'0');
while(c=getchar(),c>='0'&&c<='9') ret=ret*10+(c-'0');
ret*=sgn;
return 1;
}
int Proc(int pszIn[],int pszOut[],int len)
{
int nLen=1;
pszOut[0]=0;
int i=0;
while(i<len)
{
pszOut[nLen++]=-1;
pszOut[nLen++]=pszIn[i];
i++;
}
pszOut[nLen++]=-1;
pszOut[nLen]=-2;
return nLen;
}
void Manacher(int *p,int *str,int len)
{
int mx=0,id=0;
for(int i=0;i<len;i++)
{
p[i]=mx>i?min(p[2*id-i],mx-i):1;
while(str[i+p[i]]==str[i-p[i]])p[i]++;
if(i+p[i]>mx)
{
mx=i+p[i];
id=i;
}
}
}
int check(int len)
{
long long cnt=0;
int ret=0;
for(int i=1;i<=len;i+=2)
{
if(p[i]>2)
for(int j=(i+min(((p[i]>>1)<<1),((len>>1)/3)*2));j>i&&ret<(j-i)+1;j-=2,cnt++)
if(min(p[i],p[j])>=(j-i)+1)
ret=(j-i)+1;
}
return ret;
}
int in[MAXN],out[MAXN<<2];
int main()
{
int T,cas=1;
scan_d(T);
while(T--)
{
int n;
scan_d(n);
for(int i=0;i<n;i++)
scan_d(in[i]);
n=Proc(in,out,n);
Manacher(p,out,n);
printf("Case #%d: %d\n",cas++,(check(n)/2)*3);
}
return 0;
}