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 }