zoukankan      html  css  js  c++  java
  • [LuoguP4094] [HEOI2016] [TJOI2016]字符串(二分答案+后缀数组+ST表+主席树)

    [LuoguP4094] [HEOI2016] [TJOI2016]字符串(二分答案+后缀数组+ST表+主席树)

    题面

    给出一个长度为(n)的字符串(s),以及(m)组询问.每个询问是一个四元组((a,b,c,d)),问(s[a,b])的所有子串和字符串(s[c,d])的最长公共前缀长度的最大值。

    (n,m leq 10^5)

    分析

    显然答案有单调性。首先我们二分答案(mid),考虑如何判定。

    如果mid这个答案可行,那么一定存在一个后缀x,它的开头在([a,b-mid+1])中,且(lcp(x,s[c,d]) geq mid)

    根据我们用height数组求LCP的方法,满足条件的后缀S一定在(rank[c])向左和向右形成的连续区间内。那么我们可以用ST表+二分查找求出这个区间([p,q])

    那么问题就转化为,([a,b-mid+1])中是否存在位置的rank在([p,q])之间。因此我们可以对rank数组建出可持久化的线段树,在上面查询落在([p,q])之间的值的个数。如果个数大于0,则mid这个答案合法。

    代码

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #define maxn 100000
    #define maxs 128
    #define maxlogn 20
    #define INF 0x3f3f3f3f
    using namespace std;
    int n,m;
    char in[maxn+5];
    
    void rsort(int *ans,int *fi,int *se,int n,int m){
    	static int buck[maxn+5];
    	for(int i=0;i<=m;i++) buck[i]=0;
    	for(int i=1;i<=n;i++) buck[fi[i]]++;
    	for(int i=1;i<=m;i++) buck[i]+=buck[i-1];
    	for(int i=n;i>=1;i--) ans[buck[fi[se[i]]]--]=se[i];
    }
    int sa[maxn+5],rk[maxn+5],height[maxn+5];
    void suffix_sort(char *s,int n,int m){
    	static int se[maxn+5];
    	for(int i=1;i<=n;i++){
    		rk[i]=s[i];
    		se[i]=i;
    	}
    	rsort(sa,rk,se,n,m);
    	for(int k=1;k<=n;k*=2){
    		int p=0;
    		for(int i=n-k+1;i<=n;i++) se[++p]=i;
    		for(int i=1;i<=n;i++) if(sa[i]>k) se[++p]=sa[i]-k;
    		rsort(sa,rk,se,n,m);
    		swap(rk,se);
    		rk[sa[1]]=1;
    		p=1;
    		for(int i=2;i<=n;i++){
    			if(se[sa[i-1]]==se[sa[i]]&&se[sa[i-1]+k]==se[sa[i]+k]) rk[sa[i]]=p;
    			else rk[sa[i]]=++p;
    		}
    		if(p==n) break;
    		m=p;
    	}
    }
    void get_height(char *s,int n,int m){
    	suffix_sort(s,n,m);
    	for(int i=1;i<=n;i++) rk[sa[i]]=i;
    	int k=0;
    	for(int i=1;i<=n;i++){
    		if(k) k--;
    		int j=sa[rk[i]-1];
    		while(s[i+k]==s[j+k]) k++;
    		height[rk[i]]=k;
    	}
    }
    
    struct sparse_table{
    	int log2[maxn+5];
    	int st[maxn+5][maxlogn+5];
    	void ini(int *a,int n){
    		log2[0]=-1;
    		for(int i=1;i<=n;i++) log2[i]=log2[i>>1]+1;
    		memset(st,0x3f,sizeof(st));
    		for(int i=1;i<=n;i++) st[i][0]=a[i];
    		for(int j=1;(1<<j)<=n;j++){
    			for(int i=1;i+(1<<(j-1))-1<=n;i++){
    				st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]);
    			}
    		}
    	}
    	int query(int l,int r){
    		if(l>r) return INF;
    		int k=log2[r-l+1];
    		return min(st[l][k],st[r-(1<<k)+1][k]);
    	}
    }St;
    int lcp(int x,int y){
    	//从x位置开始的后缀,与从y位置开始的后缀的lcp 
    	if(rk[x]>rk[y]) swap(x,y);
    	return St.query(rk[x]+1,rk[y]); 
    }
    int find_lbound(int c,int val){
    	//找到height数组上从rk[c]往左值>=val的区间
    	int l=1,r=rk[c];
    	int mid,ans=r;
    	while(l<=r){
    		mid=(l+r)>>1;
    		if(St.query(mid+1,rk[c])>=val){//注意这里+1,因为求lcp求的是[rk[x]+1,rk[y]] 
    			ans=mid;
    			r=mid-1;
    		}else l=mid+1;
    	} 
    	return ans;
    }
    int find_rbound(int c,int val){
    	//找到height数组上从rk[c]往右值>=val的区间
    	int l=rk[c],r=n;
    	int mid,ans=l;
    	while(l<=r){
    		mid=(l+r)>>1;
    		if(St.query(rk[c]+1,mid)>=val){
    			ans=mid;
    			l=mid+1;
    		}else r=mid-1;
    	} 
    	return ans;
    }
    
    struct persist_segment_tree{
    #define lson(x) (tree[x].ls)
    #define rson(x) (tree[x].rs)
    	struct node{
    		int ls;
    		int rs;
    		int cnt;
    	}tree[maxn*maxlogn+5];
    	int root[maxn+5];
    	int ptr;
    	inline void push_up(int x){
    		tree[x].cnt=tree[lson(x)].cnt+tree[rson(x)].cnt;
    	} 
    	void insert(int &x,int last,int val,int l,int r){
    		x=++ptr;
    		tree[x]=tree[last];
    		if(l==r){
    			tree[x].cnt++;
    			return;
    		}
    		int mid=(l+r)>>1;
    		if(val<=mid) insert(lson(x),lson(last),val,l,mid);
    		else insert(rson(x),rson(last),val,mid+1,r);
    		push_up(x);
    	}
    	int query(int x,int L,int R,int l,int r){
    		if(L<=l&&R>=r) return tree[x].cnt;
    		int mid=(l+r)>>1;
    		int ans=0;
    		if(L<=mid) ans+=query(lson(x),L,R,l,mid);
    		if(R>mid) ans+=query(rson(x),L,R,mid+1,r);
    		return ans;
    	}
    	void ini(int *a,int n){
    		for(int i=1;i<=n;i++){
    			insert(root[i],root[i-1],a[i],0,n);
    		}
    	}
    	int Query(int pl,int pr,int vl,int vr){
    		if(vl==vr) return 0;
    		//查询数组从[pl,pr]处于[vl,vr]中的值的数量 
    //		printf("d:%d
    ",query(root[pr],vl,vr,0,n));
    //		printf("d:%d
    ",query(root[pl-1],vl,vr,0,n));
    		return query(root[pr],vl,vr,0,n)-query(root[pl-1],vl,vr,0,n);
    	}
    #undef lson
    #undef rson
    }Tr;
    
    bool check(int mid,int a,int b,int c,int d){
    	if(mid==0) return 1; 
    	int lb=find_lbound(c,mid);
    	int rb=find_rbound(c,mid);
    	if(Tr.Query(a,b-mid+1,lb,rb)>0) return 1;
    	else return 0; 
    }
    int get_ans(int a,int b,int c,int d){
    	int l=0,r=min(b-a+1,d-c+1);
    	int mid,ans=l;
    	check(1,a,b,c,d);
    	while(l<=r){
    		mid=(l+r)>>1;
    		if(check(mid,a,b,c,d)){
    			ans=mid;
    			l=mid+1;
    		}else r=mid-1;
    	}
    	return ans;
    }
    
    int main(){
    	int a,b,c,d;
    	scanf("%d %d",&n,&m);
    	scanf("%s",in+1);
    	get_height(in,n,maxs);
    	St.ini(height,n);
    	Tr.ini(rk,n);
    	for(int i=1;i<=m;i++){
    		scanf("%d %d %d %d",&a,&b,&c,&d);
    		printf("%d
    ",get_ans(a,b,c,d)); 
    	} 
    }
    /*
    7 3
    cccbbba
    3 6 3 4
    */ 
    
  • 相关阅读:
    [BZOJ1492][NOI2007]货币兑换Cash(斜率优化+CDQ分治)
    [P1768]天路(分数规划+SPFA判负环)
    [BZOJ5109][LOJ #6252][P4061][CodePlus 2017 11月赛]大吉大利,今晚吃鸡!(最短路+拓扑排序+传递闭包+map+bitset(hash+压位))
    [BZOJ1040][ZJOI2008]骑士(环套树dp)
    [P3759][TJOI2017]不勤劳的图书管理员(分块+树状数组)
    [CF665F]Four Divisors
    [LOJ6235]区间素数个数
    [SDOI2015]约数个数和
    [BZOJ3944]Sum
    [BZOJ2671]Calc
  • 原文地址:https://www.cnblogs.com/birchtree/p/12246553.html
Copyright © 2011-2022 走看看