Manacher || BZOJ 2342: [Shoi2011]双倍回文 || Luogu P4287 [SHOI2011]双倍回文
题解:具体实现时,就是在更新mr时维护前半段是回文串的最长回文串就好了
正确性的话,因为到i时如果i+RL[i]-1<=mr,那么答案肯定在i之前就维护过了;
因此只有在i+RL[i]-1>mr时需要维护答案
由于mr最多被更新N<<1次,所以时间效率是对的;在找前半段最长的回文串时,记得从外向内枚举,一旦发现答案就立即break,否则效率会假
最后,注意只需要判前半段是否满足条件就好了,一是因为我们不清楚后半段相应位置的真实RL值,二是这是个回文串,左右是相等的。
代码:
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #define max(a,b) ((a)>(b)?(a):(b)) 5 #define min(a,b) ((a)<(b)?(a):(b)) 6 using namespace std; 7 const int maxn=(5e5)+50; 8 int N,len,mid,mr,RL[maxn<<1],ans=0,a; 9 char O[maxn],S[maxn<<1]; 10 inline void Manacher(){ 11 mid=mr=0; 12 for(int i=1;i<=len;i++){ 13 if(i<mr)RL[i]=min(mr-i,RL[(mid<<1)-i]); 14 else RL[i]=1; 15 while(S[i-RL[i]]==S[i+RL[i]])RL[i]++; 16 if(i+RL[i]-1>mr){ 17 mr=i+RL[i]-1; 18 mid=i; 19 if(S[i]=='#'){ 20 for(int j=RL[i]-1;j>=2;j-=2){ 21 a=i-(j>>1); 22 if(a>=1&&S[a]=='#'&&a+RL[a]-1>=i){ 23 ans=max(ans,j); 24 break; 25 } 26 } 27 } 28 } 29 } 30 return; 31 } 32 int main(){ 33 scanf("%d",&N); 34 scanf("%s",O); 35 S[0]='$'; 36 S[len=1]='#'; 37 for(int i=0;i<N;i++){ 38 S[++len]=O[i]; 39 S[++len]='#'; 40 } 41 Manacher(); 42 printf("%d\n",ans); 43 return 0; 44 }
By:AlenaNuna