• BZOJ 4556: [Tjoi2016&Heoi2016]字符串


    4556: [Tjoi2016&Heoi2016]字符串

    Time Limit: 20 Sec  Memory Limit: 128 MB
    Submit: 789  Solved: 306
    [Submit][Status][Discuss]

    Description

    佳媛姐姐过生日的时候,她的小伙伴从某东上买了一个生日礼物。生日礼物放在一个神奇的箱子中。箱子外边写了
    一个长为n的字符串s,和m个问题。佳媛姐姐必须正确回答这m个问题,才能打开箱子拿到礼物,升职加薪,出任CE
    O,嫁给高富帅,走上人生巅峰。每个问题均有a,b,c,d四个参数,问你子串s[a..b]的所有子串和s[c..d]的最长公
    共前缀的长度的最大值是多少?佳媛姐姐并不擅长做这样的问题,所以她向你求助,你该如何帮助她呢?

    Input

    输入的第一行有两个正整数n,m,分别表示字符串的长度和询问的个数。接下来一行是一个长为n的字符串。接下来
    m行,每行有4个数a,b,c,d,表示询问s[a..b]的所有子串和s[c..d]的最长公共前缀的最大值。1<=n,m<=100,000,
    字符串中仅有小写英文字母,a<=b,c<=d,1<=a,b,c,d<=n

    Output

     对于每一次询问,输出答案。

    Sample Input

    5 5
    aaaaa
    1 1 1 5
    1 5 1 1
    2 3 2 3
    2 4 2 3
    2 3 2 4

    Sample Output

    1
    1
    2
    2
    2

    HINT

    Source

    分析:

    看到$lcp$基本第一想法就是后缀数组...

    考虑二分答案,我们二分一个长度,判断是否存在$s[a,b]$的一个子串和$s[c,d]$的$lcp>=len$,判断的时候我们找出在$ran$数组中最大的包含$ran[c]$的区间,使得这一段区间的$height$最小值$>=len$,然后判断是否存在一个后缀的下标是区间$[a,b+1-len]$中的,因为要判断一个区间内部的元素存在性问题,所以我们用主席树维护下标存在与否,也就是主席树的下标代表的是后缀的开头位置,如果存在就设为$1$...

    还是很好写的...

    代码:

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    //by NeighThorn
    using namespace std;
    
    const int maxn=100000+5,maxm=7000000+5;
    
    int n,m,tot,sa[maxn],gs[maxn],wb[maxn],wv[maxn],ran[maxn],height[maxn];
    int st[maxn][25],root[maxn],ls[maxm],rs[maxm],sum[maxm];
    
    char s[maxn];
    
    inline bool cmp(int *x,int a,int b,int l){
    	return x[a]==x[b]&&x[a+l]==x[b+l];
    }
    
    inline void da(int *sa,int *x,int n,int m){
    	int i,j,p,*y=wb;
    	for(i=0;i<m;i++) gs[i]=0;
    	for(i=0;i<n;i++) gs[x[i]]++;
    	for(i=1;i<m;i++) gs[i]+=gs[i-1];
    	for(i=n-1;~i;i--) sa[--gs[x[i]]]=i;
    	for(j=1,p=1;p<n;j<<=1,m=p){
    		for(i=n-j,p=0;i<n;i++) y[p++]=i;
    		for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j;
    		for(i=0;i<n;i++) wv[i]=x[y[i]];
    		for(i=0;i<m;i++) gs[i]=0;
    		for(i=0;i<n;i++) gs[wv[i]]++;
    		for(i=1;i<m;i++) gs[i]+=gs[i-1];
    		for(i=n-1;~i;i--) sa[--gs[wv[i]]]=y[i];
    		p=1;swap(x,y);x[sa[0]]=0;
    		for(i=1;i<n;i++) x[sa[i]]=cmp(y,sa[i],sa[i-1],j)?p-1:p++;
    	}
    }
    
    inline void calcheight(int n){
    	int i,j,k=0;
    	for(i=0;i<=n;i++) ran[sa[i]]=i;
    	for(i=0;i<n;height[ran[i++]]=k)
    		for(k?k--:233,j=sa[ran[i]-1];s[i+k]==s[j+k];k++);
    }
    
    inline bool cmp1(int x,int y){
    	return height[x]<height[y];
    }
    
    inline void prework(void){
    	for(int i=1;i<=n;i++) st[i][0]=i;
    	for(int j=1;j<=20;j++)
    		for(int i=1;i+(1<<j-1)<=n;i++)
    			st[i][j]=min(st[i+(1<<j-1)][j-1],st[i][j-1],cmp1);
    }
    
    inline int query(int x,int y){
    	if(x>y) return n;
    	int d=y-x,k;
    	for(k=20;k>=0;k--)
    		if(d>>k||k==0)
    			break;
    	return height[min(st[x][k],st[y-(1<<k)+1][k],cmp1)];
    }
    
    inline void change(int l,int r,int x,int &y,int pos){
    	y=++tot;sum[y]=sum[x]+1;
    	if(l==r) return;
    	int mid=(l+r)>>1;ls[y]=ls[x],rs[y]=rs[x];
    	if(pos<=mid)
    		change(l,mid,ls[x],ls[y],pos);
    	else
    		change(mid+1,r,rs[x],rs[y],pos);
    }
    
    inline int query(int l,int r,int x,int y,int L,int R){
    	if(sum[y]-sum[x]==0) return 0;
    	if(l==L&&R==r) return sum[y]-sum[x]>0;
    	int mid=(l+r)>>1;
    	if(R<=mid)
    		return query(l,mid,ls[x],ls[y],L,R);
    	else if(L>mid)
    		return query(mid+1,r,rs[x],rs[y],L,R);
    	else
    		return query(l,mid,ls[x],ls[y],L,mid)|query(mid+1,r,rs[x],rs[y],mid+1,R);
    }
    
    inline bool check(int a,int b,int c,int len){
    	int l=1,r=ran[c],up=ran[c],down=ran[c];
    	while(l<=r){
    		int mid=(l+r)>>1;
    		if(query(mid+1,ran[c])>=len)
    			up=mid,r=mid-1;
    		else l=mid+1;
    	}
    	l=ran[c];r=n;
    	while(l<=r){
    		int mid=(l+r)>>1;
    		if(query(ran[c]+1,mid)>=len)
    			down=mid,l=mid+1;
    		else r=mid-1;
    	}
    	if(query(0,n-1,root[up-1],root[down],a,b+1-len)) return true;
    	return false;
    }
    
    inline int solve(int a,int b,int c,int d){
    	int l=1,r=min(d-c+1,b-a+1),ans=0;
    	while(l<=r){
    		int mid=(l+r)>>1;
    		if(check(a,b,c,mid)) ans=mid,l=mid+1;
    		else r=mid-1;
    	}
    	return ans;
    }
    
    signed main(void){
    	scanf("%d%d%s",&n,&m,s);
    	for(int i=0;i<n;i++) ran[i]=(int)s[i];
    	da(sa,ran,n+1,233);calcheight(n);prework();
    	for(int i=1;i<=n;i++)
    		change(0,n-1,root[i-1],root[i],sa[i]);
    	for(int i=1,a,b,c,d;i<=m;i++){
    		scanf("%d%d%d%d",&a,&b,&c,&d);
    		printf("%d
    ",solve(a-1,b-1,c-1,d-1));
    	}
    	return 0;
    }
    

      


    By NeighThorn

     
  • 相关阅读:
    Thread类常用方法
    sql 语句NVL()用法
    SQL极限函数limit()详解<分页必备>
    查询用户上次登录时间问题
    ROWNUM-Oracle中的分页代码
    分组统计查询
    Oracle中的多表查询
    Oracle中的单行函数
    JDBC中的事务-Transaction
    MySql中增加一列
  • 原文地址:https://www.cnblogs.com/neighthorn/p/6648163.html
Copyright © 2020-2023  润新知