【单调队列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 }