• [CF1063F]String Journey[后缀数组+线段树]


    题意

    (S) 中找出 (t) 个子串满足 (t_{i+1})(t_{i}) 的子串,要让 (t) 最大。

    (|S| leq 5 imes 10^5).

    分析

    • 定义状态 (f_{i}) 表示从 (i) 出发能够得到的最长的 (journey) .

    • 容易得到最终的答案最右边的串长度一定可以是1.

    • 同时如果删掉没用的部分过后 (t_i) 的长度一定可以为 $t_{i+1} +1 $.

    • 如果在 (i) 位置存在长度为 (k) 的答案的话,将两边某一个字符在所有串中抠掉(还要舍去一个串),一定也存在长度为 (k-1) 的答案,所以答案单调。

    • 假设当前枚举的答案为 (k) ,只需要在 ([i+k,n]) 这个区间中存在一个子串满足

    [S_{i,i+1cdots i+k-2}=S_{j,j+1cdots j+k-2}$$ 或者 $$S_{i+1,i+2cdots i+k-1}=S_{j,j+1cdots j+k-2} ]

    同时 (f_jgeq k-1) 的话,就说明 (f_igeq k) .

    • 但是发现一定有 (f_ileq f_{i+1}+1) ,所以暴力枚举每个位置的答案,不需要二分。

    • 那些满足 (LCP(i,j) geq k-1) 的位置在 (sa) 数组中一定是一个区间,线段树维护最大值。

    • 总时间复杂度为 (O(nlogn))

    代码

    #include<bits/stdc++.h>
    using namespace std;
    #define go(u) for(int i=head[u],v=e[i].to;i;i=e[i].lst,v=e[i].to)
    #define rep(i,a,b) for(int i=a;i<=b;++i)
    #define repd(i,a,b) for(int i=a;i>=b;--i)
    #define pb push_back
    typedef long long LL;
    inline int gi(){
    	int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch))	{if(ch=='-') f=-1;ch=getchar();}
    	while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-48;ch=getchar();}
    	return x*f;
    }
    template<typename T>inline bool Max(T &a,T b){return a<b?a=b,1:0;}
    template<typename T>inline bool Min(T &a,T b){return b<a?a=b,1:0;}
    const int N=5e5 + 7;
    int n,ans;
    char s[N];
    int val[N<<2],f[N];
    #define Ls o<<1
    #define Rs o<<1|1
    void modify(int p,int l,int r,int o,int v){
    	Max(val[o],v);
    	if(l==r) return;	
    	int mid=l+r>>1;
    	if(p<=mid) modify(p,l,mid,Ls,v);
    	else modify(p,mid+1,r,Rs,v);
    }
    int query(int L,int R,int l,int r,int o){
    	if(L<=l&&r<=R) return val[o];
    	int mid=l+r>>1;
    	if(R<=mid) return query(L,R,l,mid,Ls);
    	if(L>mid)  return query(L,R,mid+1,r,Rs);
    	return max(query(L,R,l,mid,Ls),query(L,R,mid+1,r,Rs));
    }
    namespace SA{
    	int x[N],y[N],c[N],sa[N],h[N],mi[N][20],Log[N];
    	void getsa(int m){
    		rep(i,1,m) c[i]=0;
    		rep(i,1,n) c[x[i]=s[i]]++;
    		rep(i,1,m) c[i]+=c[i-1];
    		repd(i,n,1) sa[c[x[i]]--]=i;
    		for(int k=1;k<=n;k<<=1){
    			int p=0;	
    			for(int i=n;i>=n-k+1;--i) y[++p]=i;
    			rep(i,1,n) if(sa[i]>k) y[++p]=sa[i]-k;
    			rep(i,1,m) c[i]=0;
    			rep(i,1,n) c[x[y[i]]]++;
    			rep(i,1,m) c[i]+=c[i-1];
    			repd(i,n,1) sa[c[x[y[i]]]--]=y[i];
    			swap(x,y);p=1;x[sa[1]]=1;
    			rep(i,2,n)
    			x[sa[i]]=y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k]?p:++p;
    			if(p>=n) break; m = p;
    		}
    		rep(i,1,n) x[sa[i]]=i;
    		for(int i=1,j=0;i<=n;++i){
    			if(j) --j;if(x[i]==1) continue;
    			while(s[i+j]==s[sa[x[i]-1]+j]) ++j;
    			h[x[i]]=j;
    		}
    		Log[1]=0;
    		rep(i,2,n) Log[i]=Log[i>>1]+1;
    		rep(i,1,n) mi[i][0]=h[i];
    		for(int k=1;1<<k<=n;++k)
    		for(int i=1;i+(1<<k)-1<=n;++i)
    		mi[i][k]=min(mi[i][k-1],mi[i+(1<<k-1)][k-1]);
    	}
    	int rmq_query(int l,int r){
    		l++;
    		if(l>r) return n+1;
    		int k=Log[r-l+1];
    		return min(mi[l][k],mi[r-(1<<k)+1][k]);
    	}
    	int get1(int p,int up){
    		int l=1,r=p;
    		while(l<r){
    			int mid=l+r>>1;
    			if(rmq_query(mid,p)>=up) r=mid;
    			else l=mid+1;
    		}
    		return l;
    	}
    	int get2(int p,int up){
    		int l=p,r=n;
    		while(l<r){
    			int mid=l+r+1>>1;
    			if(rmq_query(p,mid)>=up) l=mid;
    			else r=mid-1;
    		}
    		return l;
    	}
    }
    int main(){
    	scanf("%d%s",&n,s+1);
    	using namespace SA;
    	getsa(129);
    	f[n]=ans=1;
    	for(int i=n-1,j=1;i;--i){
    		for(++j;j;--j){
    			if(i+j<=n) modify(x[i+j],1,n,1,f[i+j]);
    			int l=get1(x[i+1],j-1),r=get2(x[i+1],j-1),fg=0;
    			fg|=query(l,r,1,n,1)>=j-1;
    			l=get1(x[i],j-1),r=get2(x[i],j-1);
    			fg|=query(l,r,1,n,1)>=j-1;
    			if(fg) break;
    		}
    		f[i]=j;
    		Max(ans,f[i]);
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    一、区块链,这次不容错过
    二、常用固件升级
    2.监控利器nagios手把手企业级实战第一部
    四、NOSQL之Redis持久化缓存服务基础实战第三部
    三、NOSQL之Memcached缓存服务实战精讲第二部
    linux重装docker-compose后无法执行docker-compose命令
    mongodb启用auth,使用密码登录
    Vue的三个点es6知识,扩展运算符
    关于同一台服务器上两个PHP项目相互访问超时的问题
    ffmpeg生成视频封面图
  • 原文地址:https://www.cnblogs.com/yqgAKIOI/p/10027783.html
Copyright © 2020-2023  润新知