【单调队列DP+manacher】BZOJ2565-最长双回文串

【题目大意】

输入长度为n的串S,求S的最长双回文子串T,即可将T分为两部分X,Y,(|X|,|Y|≥1)且X和Y都是回文串。

【思路】

首先普通地求manacher,然后求出以每个位置为左端点和右端点的最长回文串长度l[i]和r[i]。

l[i]=max{2*(j-i+1)-1}(j+p[j]-1>=i),r[i]同理。显然可以用单调队列维护一下。

*网上好像大家没有用单调队列?我不清楚因为我只想出了单调队列的做法quq

然后枚举一下。为了处理方便,我们只枚举'#'位置,这样能够保证除去该'#',左边字母和'#'数量相等,右边同理。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #include<queue>
 6 using namespace std;
 7 const int MAXN=100000+50;
 8 char str[MAXN],s[MAXN*2+2];
 9 int len,p[MAXN],l[MAXN],r[MAXN];
10 
11 void init()
12 {
13     scanf("%s",str);
14     s[0]='$',s[1]='#';
15     len=strlen(str);
16     for (int i=0,j=1;i<len;i++)
17     {
18         s[++j]=str[i];
19         s[++j]='#';
20     }
21 }
22 
23 void solve()
24 {
25     int mx=0,mxid=0;
26     for (int i=1;i<2*len+2;i++)
27     {
28         if (i<mx) p[i]=(p[2*mxid-i]<=(mx-i))?p[2*mxid-i]:(mx-i);
29             else p[i]=1;
30         while (s[i+p[i]]==s[i-p[i]]) p[i]++;
31         if (i+p[i]-1>mx) mx=i+p[i]-1,mxid=i;
32     }
33 }
34 
35 void dp()
36 {
37     queue<int> que1;
38     for (int i=1;i<2*len+2;i++)
39     {
40         while (!que1.empty() && que1.front()+p[que1.front()]-1<i) que1.pop();
41         que1.push(i);
42         l[i]=2*(i-que1.front()+1)-1;
43     }
44     
45     queue<int> que2;
46     for (int i=2*len+1;i>=1;i--)
47     {
48         while (!que2.empty() && que2.front()-p[que2.front()]+1>i) que2.pop();
49         que2.push(i);
50         r[i]=2*(que2.front()-i+1)-1;
51     }
52 }
53 
54 void printans()
55 {
56     int ans=-1;
57     for (int i=1;i<2*len+2;i++) 
58         if (s[i]=='#')
59         {
60             ans=max(ans,(l[i]-1)/2+(r[i]-1)/2);
61         }
62     printf("%d",ans);
63 }
64 
65 int main()
66 {
67     init();
68     solve();
69     dp();
70     printans();
71     return 0;
72 }
原文地址:https://www.cnblogs.com/iiyiyi/p/5751978.html