• BZOJ4556 [Tjoi2016&Heoi2016]字符串 SA ST表 二分答案 主席树


    原文链接https://www.cnblogs.com/zhouzhendong/p/BZOJ4556.html

    题目传送门 - BZOJ4556

    题意

      给定一个长度为 $n$ 的字符串 $s$ 。

      有 $m$ 次询问,每次询问的格式为 $a,b,c,d$ ,问 $s[ccdots d]$ 与 $underline {s[acdots b]}$ 的所有子串 的 LCP 的最大值是多少。

      $n,mleq 10^5$

    题解

      首先,我们对于每一个询问考虑二分答案。显然答案的上下界分别是 $0$ 和 $d-c+1$ 。

      那么如何判断一个答案是否合法?

      假设答案为 $x$ ,那么我们要判断是否有满足条件的点。

      条件是:在 $[a,b-x+1]$ 为左端点的 $s$ 的后缀中,是否存在一个后缀,满足它与以 $underline{c}$ 为左端点的后缀的 LCP 不小于 $x$ 。

      于是我们考虑对后缀排序,即我们跑一跑 SA 。

      那么,满足条件最后一部分的后缀的 $rank$ 必然是连续的一段。我们可以通过倍增和预处理 ST 表来找到这个左右界 $[L,R]$。

      于是,我们就将这个判断答案是否合法的问题转化成了一个不带修改的二维数点问题。其中,一个维度是 $[a,b-x+1]$ 另一个是 $[L,R]$。

      那么我们只需要写个主席树预处理一下就可以了。

      时间复杂度 $O(nlog ^2 n)$ ,我第一发交是卡着时限过去的,后来把常数写的优美了一点,跑的不是很快,$16^+s$ 。

    代码

    #include <bits/stdc++.h>
    #define y1 _83f9
    using namespace std;
    const int N=100005;
    int read(){
    	int x=0;
    	char ch=getchar();
    	while (!isdigit(ch))
    		ch=getchar();
    	while (isdigit(ch))
    		x=(x<<1)+(x<<3)+ch-48,ch=getchar();
    	return x;
    }
    int n,m;
    int rank[N],tax[N],SA[N],tmp[N],height[N];
    int ST[N][17],Log[N];
    char s[N];
    void Sort(int n,int m){
    	for (int i=1;i<=m;i++)
    		tax[i]=0;
    	for (int i=1;i<=n;i++)
    		tax[rank[i]]++;
    	for (int i=1;i<=m;i++)
    		tax[i]+=tax[i-1];
    	for (int i=n;i>=1;i--)
    		SA[tax[rank[tmp[i]]]--]=tmp[i];
    }
    bool cmp(int rk[],int x,int y,int w){
    	return rk[x]==rk[y]&&rk[x+w]==rk[y+w];
    }
    void Suffix_Array(char s[],int n){
    	memset(SA,0,sizeof SA);
    	memset(tax,0,sizeof tax);
    	memset(tmp,0,sizeof tmp);
    	memset(rank,0,sizeof rank);
    	memset(height,0,sizeof height);
    	int m=200;
    	for (int i=1;i<=n;i++)
    		rank[i]=s[i],tmp[i]=i;
    	Sort(n,m);
    	for (int w=1,p=0;p<n;w<<=1,m=p){
    		p=0;
    		for (int i=n-w+1;i<=n;i++)
    			tmp[++p]=i;
    		for (int i=1;i<=n;i++)
    			if (SA[i]>w)
    				tmp[++p]=SA[i]-w;
    		Sort(n,m);
    		swap(tmp,rank);
    		rank[SA[1]]=p=1;
    		for (int i=2;i<=n;i++)
    			rank[SA[i]]=cmp(tmp,SA[i],SA[i-1],w)?p:++p;
    	}
    	for (int i=1,j,k=0;i<=n;height[rank[i++]]=k)
    		for (k=max(k-1,0),j=SA[rank[i]-1];s[i+k]==s[j+k];k++);
    }
    void Get_ST(int n){
    	Log[1]=0;
    	for (int i=2;i<=n;i++){
    		Log[i]=Log[i>>1]+1;
    		ST[i][0]=height[i];
    		for (int j=1;j<17;j++){
    			ST[i][j]=ST[i][j-1];
    			if (i-(1<<(j-1))>0&&ST[i-(1<<(j-1))][j-1]<ST[i][j])
    				ST[i][j]=ST[i-(1<<(j-1))][j-1];
    		}
    	}
    }
    int Query(int L,int R){
    	int d=Log[R-L+1];
    	return min(ST[L+(1<<d)-1][d],ST[R][d]);
    }
    int LCP(int x,int y){
    	x=rank[x],y=rank[y];
    	return Query(min(x,y)+1,max(x,y));
    }
    // Start to build President Tree
    const int S=N*20*2;
    int root[N],ls[S],rs[S],sum[S],tot=0;
    int newnode(){
    	tot++;
    	ls[tot]=rs[tot]=sum[tot]=0;
    	return tot;
    }
    void build(int &rt,int L,int R){
    	rt=newnode();
    	if (L==R)
    		return;
    	int mid=(L+R)>>1;
    	build(ls[rt],L,mid);
    	build(rs[rt],mid+1,R);
    }
    void update(int prt,int &rt,int L,int R,int x,int v){
    	if (!rt||rt==prt)
    		rt=newnode(),sum[rt]=sum[prt];
    	sum[rt]+=v;
    	if (L==R)
    		return;
    	if (!ls[rt])
    		ls[rt]=ls[prt];
    	if (!rs[rt])
    		rs[rt]=rs[prt];
    	int mid=(L+R)>>1;
    	if (x<=mid)
    		update(ls[prt],ls[rt],L,mid,x,v);
    	else
    		update(rs[prt],rs[rt],mid+1,R,x,v);
    }
    int query(int prt,int rt,int L,int R,int xL,int xR){
    	if (L>xR||R<xL)
    		return 0;
    	if (xL<=L&&R<=xR)
    		return sum[rt]-sum[prt];
    	int mid=(L+R)>>1;
    	return query(ls[prt],ls[rt],L,mid,xL,xR)
    		  +query(rs[prt],rs[rt],mid+1,R,xL,xR);
    }
    int Query(int x1,int x2,int y1,int y2){
    	return query(root[x1-1],root[x2],1,n,y1,y2);
    }
    bool check(int k,int x,int a,int b){
    	if (k==0)
    		return 1;
    	if (b-a+1<k)
    		return 0;
    	int L=x,R=x;
    	for (int i=16;i>=0;i--){
    		if (L-(1<<i)>=1&&Query(L-(1<<i)+1,x)>=k)
    			L-=1<<i;
    		if (R+(1<<i)<=n&&Query(x+1,R+(1<<i))>=k)
    			R+=1<<i;
    	}
    	return Query(a,b-k+1,L,R);
    }
    int main(){
    	n=read(),m=read();
    	scanf("%s",s+1);
    	Suffix_Array(s,n);
    	Get_ST(n);
    	build(root[0],1,n);
    	for (int i=1;i<=n;i++)
    		update(root[i-1],root[i],1,n,rank[i],1);
    	while (m--){
    		int L1=read(),R1=read(),L2=read(),R2=read();
    		int L=0,R=R2-L2+1,mid,ans=0;
    		while (L<=R){
    			int mid=(L+R)>>1;
    			if (check(mid,rank[L2],L1,R1))
    				L=mid+1,ans=mid;
    			else
    				R=mid-1;
    		}
    		printf("%d
    ",ans);
    	}
    	return 0;
    }
    

      

  • 相关阅读:
    (三)xpath爬取4K高清美女壁纸
    聚焦爬虫:数据解析
    (二)requests-爬取国家药监局生产许可证数据
    (一)requests-实战小练习
    requests模块
    spring+apache dbcp +oracle 连接池配置以及优化
    IntelliJ IDEA 注释模版 输入/**后 不显示配置好的模板
    oracle 隔离级别、事务怎么开始的以及如何查看数据库采用字符集
    java 日期处理相关
    Oracle 插入数据时获取系统时间
  • 原文地址:https://www.cnblogs.com/zhouzhendong/p/BZOJ4556.html
Copyright © 2020-2023  润新知