AlenaNuna

导航

Manacher || BZOJ 2342: [Shoi2011]双倍回文 || Luogu P4287 [SHOI2011]双倍回文

题面:[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

 

posted on 2019-05-21 19:20  AlenaNuna  阅读(129)  评论(0编辑  收藏  举报