zoukankan      html  css  js  c++  java
  • bzoj 2565: 最长双回文串【manacher+线段树】

    因为我很愚蠢所以用了很愚蠢的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
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    Redis配置文件的使用
    WEB请求处理一:浏览器请求发起处理
    Nginx配置文件(nginx.conf)配置详解
    【node】------mongoose的基本使用
    Promise.resolve()与new Promise(r => r(v))
    promise是什么?
    apiDoc
    apiDoc 使用指南
    微信小程序-性能与体验优化
    微信小程序-调取上一页的方法
  • 原文地址:https://www.cnblogs.com/lokiii/p/10011996.html
Copyright © 2011-2022 走看看