题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4513
解题思路:
寻找两边向回文中心非递减的最长回文子串。
manacher板子里匹配下一位的时候增加一个判断条件就行了,不仅判断两位相同,额外判断靠左的那个点右移两个位置的点是否>=当前位置。
代码:
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define for0(i,a,b) for (int i=a;i<b;i++)
#define for1(i,a,b) for (int i=a;i<=b;i++)
const int maxn=100010;
int str[maxn];
int tmp[maxn<<1];
int Len[maxn<<1];
int INIT(int *st,int len)
{
int i;
tmp[0]=-2;
for(i=1;i<=2*len;i+=2)
{
tmp[i]=-1;
tmp[i+1]=st[i/2];
}
tmp[2*len+1]=-1;
tmp[2*len+2]=-3;
tmp[2*len+3]=0;
return 2*len+1;
}
int MANACHER(int *st,int len)
{
int mx=0,ans=0,po=0;//mx即为当前计算回文串最右边字符的最大值
for(int i=1;i<=len;i++)
{
if(mx>i)
Len[i]=min(mx-i,Len[2*po-i]);//在Len[j]和mx-i中取个小
else
Len[i]=1;//如果i>=mx,要从头开始匹配
while(st[i-Len[i]]==st[i+Len[i]] && st[i-Len[i]]<=st[i-Len[i]+2] )
Len[i]++;
if(Len[i]+i>mx)//若新计算的回文串右端点位置大于mx,要更新po和mx的值
{
mx=Len[i]+i;
po=i;
}
ans=max(ans,Len[i]);
}
return ans-1;//返回Len[i]中的最大值-1即为原串的最长回文子串额长度
}
int main()
{
int T;
scanf("%d",&T);
while (T--){
int n;
scanf("%d",&n);
for0(i,0,n) scanf("%d",str+i);
int len = INIT(str,n);
printf("%d\n",MANACHER(tmp,len));
}
return 0;
}