• jzoj 4614. 【NOIP2016模拟7.12】字符串


    Question

    在这里插入图片描述

    Solution

    这题是道后缀数组的题目。
    对于原串,我们先搞一遍后缀数组并求出heightheight数组。
    由于我用的是线段树,所以我们应当把询问按照a从大到小排个序。
    线段树的叶子节点ii表示rank[i]rank[i]的位置,对于非叶子节点则维护其区间的最小值。(初始值为无穷大)
    我们倒着枚举串中的位置,每到一个位置,便将它在线段树所在的位置赋值为在串中的位置。
    如果当前的位置为某个询问的aa,那么我们可以对其求出答案。
    我们先二分答案,由于rankrank的一个性质:
    对于两个后缀,如果其rankrank的差的绝对值越小,则其公共前缀长度越长。
    所以我们可以对于该询问的cc,求出其排名前后都与其公共前缀长度>=midmid的范围。
    可以用RMQ来求。
    而后,我们可以在线段树里面查找该范围的最小值,
    看是否有在[a,bmid+1][a,b-mid+1]这一段区间里面的数即可。

    Code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define N 100010
    using namespace std;
    int t[N<<6];
    struct num{int a,b,c,d,fr;}a[N];
    int n,m,M,rk[N],tp[N],tax[N],sa[N];
    int h[N],rt[N],tot=0,ma[N][17],out[N];
    char s[N];
    
    inline int read()
    {
    	int x=0; char c=getchar();
    	while (c<'0' || c>'9') c=getchar();
    	while (c>='0' && c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    	return x;
    }
    
    void qsort()
    {
    	for (int i=0;i<=M;i++) tax[i]=0;
    	for (int i=1;i<=n;i++) tax[rk[i]]++;
    	for (int i=1;i<=M;i++) tax[i]+=tax[i-1];
    	for (int i=n;i>0;i--) sa[tax[rk[tp[i]]]--]=tp[i];
    }
    
    void hzsz()
    {
    	scanf("%s",s+1);M=27;
    	for (int i=1;i<=n;i++) rk[i]=s[i]-'a'+1,tp[i]=i;
    	qsort();
    	for (int w=1,p;p!=n;w<<=1,M=p)
    	{
    		p=0;
    		for (int i=n-w+1;i<=n;i++) tp[++p]=i;
    		for (int i=1;i<=n;i++)
    			if (sa[i]>w) tp[++p]=sa[i]-w;
    		qsort();
    		memcpy(tp,rk,sizeof(rk));
    		rk[sa[1]]=p=1;
    		for (int i=2;i<=n;i++)
    		{
    			if (tp[sa[i-1]]!=tp[sa[i]] || tp[sa[i-1]+w]!=tp[sa[i]+w]) p++;
    			rk[sa[i]]=p;
    		}
    	}
    	for (int i=1,j,k=0;i<=n;i++)
    	{
    		if (k) k--;
    		j=sa[rk[i]-1];
    		while (s[i+k]==s[j+k]) k++;
    		h[rk[i]]=k;
    	}
    }
    
    inline int cmp(num a,num b) {return a.a<b.a;}
    
    void RMQ()
    {
    	for (int i=1;i<=n;i++) ma[i][0]=h[i+1];
    	for (int j=1;j<=16;j++)
    		for (int i=1;i+(1<<j-1)<=n;i++)
    				ma[i][j]=std::min(ma[i][j-1],ma[i+(1<<j-1)][j-1]);
    }
    
    void build(int x,int l,int r)
    {
    	t[x]=1010580540;
    	if (l==r) return;
    	int mid=l+r>>1;
    	build(x<<1,l,mid);
    	build(x<<1|1,mid+1,r);
    }
    
    void insert(int x,int l,int r,int f,int to)
    {
    	t[x]=min(t[x],to);
    	if (l==r) return;
    	int mid=l+r>>1;
    	if (f<=mid) insert(x<<1,l,mid,f,to);
    	else insert(x<<1|1,mid+1,r,f,to);
    }
    
    int getmin(int x,int l,int r,int fl,int fr)
    {
    	if (fl<=l && fr>=r) return t[x];
    	int mid=l+r>>1,s=1010580540;
    	if (fl<=mid) s=std::min(s,getmin(x<<1,l,mid,fl,fr));
    	if (fr>mid) s=std::min(s,getmin(x<<1|1,mid+1,r,fl,fr));
    	return s;
    }
    
    int main()
    {
    	freopen("string.in","r",stdin);
    	freopen("string.out","w",stdout);
    	n=read(),m=read();
    	hzsz();RMQ();
    	for (int i=1;i<=m;i++)
    		a[i].a=read(),a[i].b=read(),a[i].c=read(),a[i].d=read(),a[i].fr=i;
    	std::sort(a+1,a+m+1,cmp);
    	build(1,1,n);
    	for (int i=n,now=m,l,r,mid,le,ri;i!=0;i--)
    	{
    		insert(1,1,n,rk[i],i);
    		while (a[now].a==i)
    		{
    			l=0,r=min(a[now].d-a[now].c+1,a[now].b-a[now].a+1);
    			while (l<=r)
    			{
    				mid=l+r>>1;le=ri=rk[a[now].c];
    				for (int i=16;i>=0;i--)
    					if (le>=(1<<i) && ma[le-(1<<i)][i]>=mid) le-=(1<<i);
    				for (int i=16;i>=0;i--)
    					if (ri+(1<<i)<=n && ma[r][i]>=mid) ri+=(1<<i);
    				if (getmin(1,1,n,le,ri)<=a[now].b-mid+1) l=mid+1;
    				else r=mid-1;
    			}
    			out[a[now--].fr]=l-1;
    		}
    	}
    	for(int i=1;i<=m;i++)
    		printf("%d
    ",out[i]);
    	return 0;
    }
    
    转载需注明出处。
  • 相关阅读:
    求助:多个参数的存储过程
    经典回顾:哲学家进餐问题(The Dinning Philosophers Problem)
    杨辉三角
    开发公共课选修系统之二
    关于即时消息系统
    用户 'NT AUTHORITY/NETWORK SERVICE' 登录失败 的解决方法(转)
    细说反射API
    东莞哪家公司可以提供实习的机会?
    使用Gmail发送email时出现Must issue a STARTTLS command first错误!!
    Revolution
  • 原文地址:https://www.cnblogs.com/jz929/p/11817519.html
Copyright © 2020-2023  润新知