?
題意:給一個序列,找出1個連續子序列,將其平分成前,中,后等長的3段子序列,要求【前】和【中】是回文,【中】和【后】是回文。求3段最長為多少?由于平分的關系,所以答案應該是3的倍數。
?
思路:先Manacher求最長子串,利用期間所記錄的P 數組,窮舉一下所有可能的前兩串,再用O(1)時間判斷第3串是否符合要求。
具體做法:
(1)P[i]記錄的是以i為中心,從i-P[i]+1到i+P[i]-1這段都是回文。由于前兩段之和必為偶數,所以必須選取str[i]為'#'的。
(2)掃一遍每個'#',以其最長的回文開始窮舉(僅需將P[i]--即可,然后找到右邊對應的'#',判斷P[i]是不是大于所窮舉的長度),直到3段都滿足要求了,跳出此‘#’,換下一個。
?
?


1 #include <iostream> 2 #include <cstring> 3 #include <vector> 4 #include <stdio.h> 5 using namespace std; 6 const int N=100010; 7 int n; 8 int s[N*2]; 9 int P[N*2]; 10 11 12 int cal(int len) 13 { 14 if(n<3) return 0; 15 memset(P,0,sizeof(P)); 16 int id=1, mx=1; 17 P[0]=1; 18 P[1]=1; 19 for(int i=2; i<len; i++) 20 { 21 P[i] =i>mx? 1: min( P[2*id-i], mx-i); 22 while(s[i+P[i]]==s[i-P[i]]) P[i]++; 23 if(i+P[i]>mx) 24 { 25 id=i; 26 mx=i+P[i]; 27 } 28 } 29 int ans=0; 30 for(int i=3; i+4<len; i+=2) 31 { 32 int tag=P[i]-1; 33 if( tag>1 && tag>ans ) 34 { 35 while( P[i+tag]<=tag && tag>ans ) tag--; 36 if(tag>ans) ans=tag; 37 } 38 } 39 40 return ans/2*3; 41 } 42 43 int main() 44 { 45 //freopen("input.txt", "r", stdin); 46 int t, tmp, j=0; 47 cin>>t; 48 49 while(t--) 50 { 51 scanf("%d", &n); 52 s[0]=-1; 53 s[1]=-2; 54 for(int i=0,j=2; i<n; i++) 55 { 56 scanf("%d",&tmp); 57 s[j++]=tmp; 58 s[j++]=-2; 59 } 60 s[n*2+2]=-30; 61 62 printf("Case #%d: %d\n", ++j, cal( n*2+2 )); 63 } 64 return 0; 65 }
?