洛谷 P4555 [国家集训队]最长双回文串(Manacher)

题目链接:https://www.luogu.com.cn/problem/P4555

 

首先明白两个回文串,那么要使两个回文串成立,那么我们只能把$'#'$作为中间节点。

然后我们跑一边Manacher,记录$l[],r[]$,$l[i]$表示以$i$开头的最长回文串长度,$r[i]$表示以$i$结尾的最长回文串长度。

那么到最后我们只需要用线性的时间来枚举$i$,找$l_i+r_i$最大即可。

但是,在Manacher算法中有局限性:就是我们处理出来的$l,r$都是饱和回文串的,那么我们就要处理不饱和回文串:

$l[i]=max(l[i],l[i-2]-2)$

$r[i]=max(r[i],r[i+2]-2)$

解释一下$1$式,$2$式类似:

其实都是一个递推的过程,l[i-2]即为上一个$‘#’$的位置,$-2$是因为回文串的对称性。

 

AC代码:

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<algorithm>
 4 #include<cstring>
 5 using namespace std;
 6 
 7 const int N=100010;
 8 int p[N*2],l[N*2],r[N*2];
 9 char s[N*2],str[N];
10 int ans,t;
11 
12 void manacher(int len){
13     s[0]='$'; s[++t]='#';
14     for(int i=1;i<=len;i++){
15         s[++t]=str[i];
16         s[++t]='#';
17     }
18     int pos=0,mx=0;
19     for(int i=1;i<=t;i++){
20         if(i>mx) p[i]=1;
21         else p[i]=min(p[2*pos-i],mx-i);
22         while(i+p[i]<=t&&i-p[i]>=1&&s[i-p[i]]==s[i+p[i]]) p[i]++;
23         if(i+p[i]>mx){
24             mx=i+p[i];
25             pos=i;
26         }
27         l[i-p[i]+1]=max(l[i-p[i]+1],p[i]-1);
28         r[i+p[i]-1]=max(r[i+p[i]-1],p[i]-1);
29     }
30 }
31 
32 int main(){
33     scanf("%s",str+1);
34     manacher(strlen(str+1));
35     for(int i=3;i<=t;i+=2) l[i]=max(l[i],l[i-2]-2);
36     for(int i=t;i>=3;i-=2) r[i]=max(r[i],r[i+2]-2);
37     for(int i=3;i<=t;i+=2) if(l[i]&&r[i]) ans=max(ans,l[i]+r[i]);
38     printf("%d\n",ans);
39     return 0;
40 }
AC代码

 

posted @ 2020-02-19 22:01  dfydn  阅读(229)  评论(0编辑  收藏  举报