这题说的找出一个数组串 3等分 第一个部分和第3个部分一样,第二个部分和第一个部分回文,那么计算出这些字符串问这样的字符串最长为多少,我们先使用manacher 计算出每个位置以他为对称轴左边端点的最长回文半径 加入第i个位置 回文半径为 d[i],那么他能影响的范围为d[i]-i至i 如果他是那个最大的那么答案就是 i-j (j为[d[i]-i,i]之间的数),我们可以再适当的点加入这个位置的影响在线段树中,然后在最后使用线段树查找出这个区间的最值是多少
#include <iostream> #include <algorithm> #include <cstdio> #include <string.h> #include <vector> using namespace std; const int maxn=100005; int str[maxn]; int s[maxn*2]; int rad[maxn*2];//以i为中心的回文串 半径 int H[maxn],to[maxn],nx[maxn],numofedg; int d[maxn]; void addedg(int u, int v) { ++numofedg; to[numofedg]=v; nx[numofedg]=H[u]; H[u]=numofedg; } void manacher(int len) { int cur=1; s[0]=-1; for(int i=0; i<len; i++) { s[cur++]=str[i]; s[cur++]=-1; } rad[0]=1; int id=0; for(int i=1; i<cur; i++) { if(rad[id]+id>i)rad[i]=min(rad[2*id-i],rad[id]+id-i); else rad[i]=1; while(i-rad[i]>=0&&i+rad[i]<cur&&s[ i - rad[i] ] == s[ i+rad[i] ])rad[i]++; if(id+rad[i]<i+rad[i])id=i; } for(int i=1; i<=len; i++) { d[i]=rad[i*2]/2; if(d[i]==0)continue; addedg(i-d[i],i); } } struct Itree { int loc,cL,cR,ans; int val[maxn*4]; void build(int L, int R, int o) { val[o]=0; if(L==R) return ; int mid=(L+R)>>1; build(L,mid,o*2); build(mid+1,R,o*2+1); } void update(int L, int R, int o) { if(L==R) { val[o]=L ; return ; } int mid=(L+R)>>1; if(loc<=mid) update(L,mid,o*2); else update(mid+1,R,o*2+1); val[o]=max(val[o*2],val[o*2+1]); } void query(int L, int R, int o) { if(cL<=L&&R<=cR) { ans=max(ans,val[o]); return; } int mid=(L+R)>>1; if(cL<=mid)query(L,mid,o*2); if(cR>mid) query(mid+1,R,o*2+1); } }T; int main() { int cas; scanf("%d",&cas); for(int cc=1; cc<=cas; cc++) { int n; scanf("%d",&n); H[0]=0;numofedg=0; for(int i=0; i<n; i++){ H[i+1]=0; scanf("%d",&str[i]); } manacher(n); int ans=0; T.build(1,n,1); d[0]=0; for(int i=0; i<n; i++) { for(int j=H[i]; j; j=nx[j]) { T.loc=to[j]; T.update(1,n,1); } T.cL=i+1;T.cR=d[i]+i; if(T.cL>T.cR)continue; T.ans=0; T.query(1,n,1); ans=max(ans,T.ans-i); } printf("Case #%d: %d\n",cc,ans*3); } return 0; }
时间: 2024-10-12 18:27:14