BZOJ 2565 最长双回文串 (Manacher)

题目大意:一个双回文串被定义为,这个串在某个位置断开,前面一段和后面一段都是回文串,求最长的双回文串

并没有想出怎么线性求出$R_{i}$数组= =,只想了个线段树,但竟然要区间修改等差数列再维护最大值,我并不会,一看就不是正解

正解是先用$Manacher$求出以$i$为回文中心,最长的回文半径$p_{i}$

定义$L_{i}$表示以i结尾最长的回文串,$R_{i}$表示以$i$开头最长的回文串

在求$p_{i}$的过程中,我们只需要求出回文边界的$L,R$

$L_{i+p_{i}-1}=max(p_{i}-1)$,$R_{i-p_{i}+1}=max(p_{i}-1)$

再正向/反向扫一次求出每个位置实际的$L,R$

$L_{i}=max(L_{i},L{i+2}-2)$,$R_{i}=max(R_{i},R_{i-2}+2)$

由于使用了$Manacher$算法,故能作为回文边界的只能是$'#'$,所以从头到尾扫$'#'$统计答案即可

 1 #include <cmath>
 2 #include <queue>
 3 #include <vector>
 4 #include <cstdio>
 5 #include <cstring>
 6 #include <algorithm>
 7 #define NN 200100
 8 #define MM 1510
 9 #define ll long long
10 #define dd double  
11 #define uint unsigned int
12 #define mod 1000000007
13 #define idx(X) (X-'a')
14 #define eps (1e-9)
15 using namespace std;
16  
17 int n,nn;
18 char str[NN],a[NN];
19 int p[NN],L[NN],R[NN];
20  
21 int main()
22 {
23     scanf("%s",str+1);
24     n=strlen(str+1);
25     a[1]='#';
26     for(int i=1;i<=n;i++)
27         a[2*i]=str[i],a[2*i+1]='#';
28     nn=2*n+1;p[1]=1;
29     int mr=2,mid=1;
30     for(int i=2;i<=nn;i++){
31         if(i<mr) p[i]=min(p[2*mid-i],mr-i);
32         else p[i]=1;
33         while(a[i-p[i]]==a[i+p[i]]) p[i]++;
34         if(i+p[i]>mr) 
35             mr=i+p[i],mid=i;
36         L[i+p[i]-1]=max(L[i+p[i]-1],p[i]-1);
37         R[i-p[i]+1]=max(R[i-p[i]+1],p[i]-1);
38     }
39     for(int i=1;i<=nn;i+=2)
40         R[i]=max(R[i],R[i-2]-2);
41     for(int i=nn;i>=1;i-=2)
42         L[i]=max(L[i],L[i+2]-2);
43     int ans=0;
44     for(int i=1;i<=nn;i+=2)
45         ans=max(ans,R[i]+L[i]);
46     printf("%d\n",ans);    
47     return 0;
48 }

 

posted @ 2018-11-26 16:35  guapisolo  阅读(142)  评论(0编辑  收藏  举报