bzoj 2565: 最長雙迴文串【manacher+線段樹】
阿新 • • 發佈:2018-11-24
因為我很愚蠢所以用了很愚蠢的O(nlogn)的manacher+線段樹做法
就是開兩個線段樹mn和mx分別表示左端點在i的最長迴文子串和右端點在i的最長迴文子串
用manacher求出每個點的最長迴文子串,然後對於一組(i,f[i])(這裡的i是加完#之後的串),我們考慮對原串貢獻是對於中點右邊一段迴文串上點x的mn貢獻i-2x+1,x最後加就變成線上段樹上貢獻i+1,然後同理對左邊一段貢獻2x-i+1,線上段樹上貢獻-i+1,注意這裡要分一下奇偶還有仔細算一下邊界
然後列舉斷點,線上段樹上查,取max即可
實際上注意到是可以O(n)的,mnmx更新時候的範圍超過之後就變成負的沒有意義了,所以直接更新區間端點,最後把i%2相同的向前/向後更新一下即可
或者直接用迴文自動機預處理
#include<iostream> #include<cstdio> #include<cstring> using namespace std; const int N=200005; int n,f[N],ans; char c[N],s[N]; struct xds { int l,r,mx,lz; }t[N<<2]; struct wk { xds t[N<<2]; void build(int ro,int l,int r) { t[ro].l=l,t[ro].r=r,t[ro].lz=t[ro].mx=-1e9; if(l==r) return; int mid=(l+r)>>1; build(ro<<1,l,mid); build(ro<<1|1,mid+1,r); } void pd(int ro) { if(t[ro].lz!=-1e9) { t[ro<<1].mx=max(t[ro<<1].mx,t[ro].lz); t[ro<<1].lz=max(t[ro<<1].lz,t[ro].lz); t[ro<<1|1].mx=max(t[ro<<1|1].mx,t[ro].lz); t[ro<<1|1].lz=max(t[ro<<1|1].lz,t[ro].lz); t[ro].lz=-1e9; } } void update(int ro,int l,int r,int v) {//cerr<<l<<" "<<r<<endl; if(l>r) return; if(t[ro].l==l&&t[ro].r==r) { t[ro].mx=max(t[ro].mx,v); t[ro].lz=max(t[ro].lz,v); return; } pd(ro); int mid=(t[ro].l+t[ro].r)>>1; if(r<=mid) update(ro<<1,l,r,v); else if(l>mid) update(ro<<1|1,l,r,v); else update(ro<<1,l,mid,v),update(ro<<1|1,mid+1,r,v); t[ro].mx=max(t[ro<<1].mx,t[ro<<1|1].mx); } int ques(int ro,int p) { if(t[ro].l==t[ro].r) return t[ro].mx; pd(ro); int mid=(t[ro].l+t[ro].r)>>1; if(p<=mid) return ques(ro<<1,p); else return ques(ro<<1|1,p); } }mn,mx; int main() { scanf("%s",c+1); n=strlen(c+1); for(int i=1;i<=n;i++) s[2*i]=c[i],s[2*i+1]='#'; s[0]='$',s[1]='#',s[2*n+2]='%'; int mxw=0,w; mn.build(1,1,n),mx.build(1,1,n); for(int i=1;i<2*n+2;i++) { if(i<mxw) f[i]=min(f[2*w-i],mxw-i); else f[i]=1; for(;s[i+f[i]]==s[i-f[i]];f[i]++); if(i+f[i]>mxw) mxw=i+f[i],w=i; if(s[i]=='#') mx.update(1,max(1,(i+1)/2),min(n,(i+1)/2+(f[i]-1)/2-1),-i+1),mn.update(1,max(1,(i-1)/2-(f[i]-1)/2+1),min(n,(i-1)/2),i+1); else mx.update(1,max(1,i/2),min(n,i/2+f[i]/2-1),-i+1),mn.update(1,max(1,i/2-f[i]/2+1),min(n,i/2),i+1); } for(int i=2;i<=n;i++) ans=max(ans,mx.ques(1,i-1)+2*(i-1)+mn.ques(1,i)-2*i);//,cerr<<i<<" "<<mn.ques(1,i)-2*i<<" "<<mx.ques(1,i)+2*i<<endl;; printf("%d\n",ans); return 0; }