zoukankan      html  css  js  c++  java
  • 题解 TJOI/HEOI2016 字符串

    题解 TJOI/HEOI2016 字符串

    题面

    loj

    解析

    二分答案 (mid).

    考虑后缀数组求出的数组,那么只要看 (sa) 数组里的

    包含 (c) 开头的后缀的

    一段满足最长公共前缀 (geq mid) 的区间里,有没有开头在 ([a,b-mid+1]) 中的后缀即可.

    (可能上面那段话太模糊,请继续看下去)

    具体来说,先求出一个 (l),满足 (sa) 数组中 ([l,c]) 这段区间的最长公共前缀都 (geq mid).

    再求 (r),满足 (sa) 数组中 (c,r) 这段区间的最长公共前缀 (geq mid).

    那么如果有开头

    (sa) 数组中的 ([l,r]) 范围内,

    且在原串中 ([a,b-mid+1])

    的后缀,

    则答案 (geq mid).

    那么 (l,r) 可以在求出 (height) 数组的 ( exttt{st}) 表后,二分答案求.

    而判断 ([l,r]) 中是否有开头在 ([a,b-mid+1]) 中的点的后缀,

    可以以 (rk) 作为下标,建主席树,

    每次查询 ([a,b-mid+1]) 中的下标在 ([l,r]) 中的个数,如果 (>0) 则说明存在.

    这题需要对后缀数组,主席树的灵活运用,并需要一些做题技巧(二分答案).

    code

    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #define ll long long
    using namespace std;
    
    inline int read(){
    	int sum=0,f=1;char c=getchar();
    	while(c>'9'||c<'0'){if(c=='-') f=-1;c=getchar();}
    	while(c<='9'&&c>='0'){sum=sum*10+c-'0';c=getchar();}
    	return f*sum;
    }
    
    const int N=200005;
    int n,Q;
    char ss[N];
    
    namespace SA{
    	int a[N],b[N],ca[N],cb[N],st[N];
    	int sa[N],rk[N],ht[N];
    	int lg[N],s[N<<1][18];//s_table
    	inline void init(){
    		int m=n;
    		for(int i=1;i<=n;i++) ca[a[i]=ss[i]-'a'+1]++;
    		for(int i=1;i<=m;i++) ca[i]+=ca[i-1];
    		for(int i=n;i>=1;i--) sa[ca[a[i]]--]=i;
    		rk[sa[1]]=1;
    		for(int i=2;i<=n;i++)
    			rk[sa[i]]=rk[sa[i-1]]+(a[sa[i]]!=a[sa[i-1]]);
    		for(int l=1;rk[sa[n]]<n;l<<=1){
    			for(int i=0;i<=m;i++) ca[i]=cb[i]=0;
    			for(int i=1;i<=n;i++){
    				ca[a[i]=rk[i]]++;
    				cb[b[i]=(i+l<=n? rk[i+l]:0)]++;
    			}
    			for(int i=1;i<=m;i++) ca[i]+=ca[i-1],cb[i]+=cb[i-1];
    			for(int i=1;i<=n;i++) st[cb[b[i]]--]=i;
    			for(int i=n;i>=1;i--) sa[ca[a[st[i]]]--]=st[i];
    			rk[sa[1]]=1;
    			for(int i=2;i<=n;i++)
    				rk[sa[i]]=rk[sa[i-1]]+(a[sa[i]]!=a[sa[i-1]]||b[sa[i]]!=b[sa[i-1]]);
    		}
    		int k=0;
    		for(int i=1;i<=n;i++){
    			if(rk[i]==1) continue;
    			if(k) k--;
    			int j=sa[rk[i]-1];
    			while(max(i,j)+k<=n&&ss[i+k]==ss[j+k]) k++;
    			ht[rk[i]]=k;
    	 	}
    		for(int i=1;i<=n;i++) s[i][0]=ht[i];
    		for(int j=1;j<18;j++)
    			for(int i=1;i<=n;i++)
    				if(i+(1<<(j-1))<=n) s[i][j]=min(s[i][j-1],s[i+(1<<(j-1))][j-1]);
    		lg[0]=-1;
    		for(int i=1;i<=n;i++) lg[i]=lg[i>>1]+1;
    	}
    	inline int lcp(int l,int r){
    		if(l>r) return 0x3f3f3f3f;
    		int k=lg[r-l+1];
    		return min(s[l][k],s[r-(1<<k)+1][k]);
    	}
    };
    using namespace SA;
    
    namespace tr{
    	struct tree{int ls,rs,sum;}t[N*40];
    	int rt[N],tot;
    	inline void insert(int o,int &p,int l,int r,int x){
    		p=++tot;t[p]=t[o];t[p].sum++;
    		if(l==r) return ;
    		int mid=(l+r)>>1;
    		if(x<=mid) insert(t[o].ls,t[p].ls,l,mid,x);
    		else insert(t[o].rs,t[p].rs,mid+1,r,x);
    	}
    	inline int query(int p1,int p2,int l,int r,int ql,int qr){
    		if(ql>qr||!(t[p2].sum-t[p1].sum)) return 0;
    		if(l>=ql&&r<=qr) return t[p2].sum-t[p1].sum;
    		int mid=(l+r)>>1,ret=0;
    		if(ql<=mid) ret+=query(t[p1].ls,t[p2].ls,l,mid,ql,qr);
    		if(qr>mid) ret+=query(t[p1].rs,t[p2].rs,mid+1,r,ql,qr);
    		return ret;
    	}
    };
    using namespace tr;
    
    signed main(){
    	n=read();Q=read();
    	scanf("%s",ss+1);
    	init();
    	for(int i=1;i<=n;i++) insert(rt[i-1],rt[i],1,n,rk[i]);
    	while(Q--){
    		int a=read(),b=read();
    		int c=read(),d=read();
    		int L=0,R=min(b-a+1,d-c+1),ans=0;
    		while(L<=R){
    			int mid=(L+R)>>1;
    			int pl=0,pr=0;
    			int l=1,r=rk[c];
    			while(l<=r){
    				int md=(l+r)>>1;
    				if(lcp(md+1,rk[c])>=mid) r=md-1,pl=md;
    				else l=md+1;
    			}
    			l=rk[c];r=n;
    			while(l<=r){
    				int md=(l+r)>>1;
    				if(lcp(rk[c]+1,md)>=mid) l=md+1,pr=md;
    				else r=md-1;
    			}
    			if(query(rt[a-1],rt[(mid? b-mid+1:b)],1,n,pl,pr)) ans=mid,L=mid+1;
    			else R=mid-1;
    		}
    		printf("%d
    ",ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    人工智能实战2019第一次作业_李大
    300道四则运算题目
    build to win读后感
    C# WinForm(简易计算器示例)
    WPF关于Calendar的Style
    winform下实现Label在窗体中从右到左不断循环的动画效果
    WPF 选中datagrid列头中checkbox则全选对应整列checkbox
    Window下android 模拟器SD卡的使用方法
    Linux下在Android模拟器中使用SD 卡的操作步骤
    WPF之VLC流媒体播放
  • 原文地址:https://www.cnblogs.com/permzf/p/13125370.html
Copyright © 2011-2022 走看看