zoukankan      html  css  js  c++  java
  • [BZOJ 3230]相似子串

    [BZOJ 3230]相似子串

    题意

    给定一个长度为 (n) 的字符串以及 (q) 组查询, 每组查询给定 (a)(b), 求在所有本质不同子串中排名第 (a) 和第 (b) 的串的最长公共前缀与最长公共后缀的平方和.

    (n,qle 1 imes 10^5).

    题解

    后缀数组板子题.麻麻我终于会用后缀数组辣

    本来想接着用SAM的...但是发现多组查询排名为 (k) 的本质不同子串以我对SAM的理解好像不能做...于是就用了SA.

    最长公共前后缀显然对正反串建两个SA就可以 (O(nlog n) ightarrow O(1)) 算了. 关键在于怎么找到这两个子串.

    把所有后缀排序后, 显然每一个后缀对本质不同子串的贡献就是和前一个后缀相同的部分之外的部分了. 那么求出所有 (height) 后我们也能知道前 (k) 个后缀总共产生了多少本质不同的子串. 直接二分找到子串左端点然后根据差值算出子串长度就可以了.

    因为要有两个SA, 所以简单地封到结构体里会比较好写. 然而这根本不算封装

    参考代码

    #include <bits/stdc++.h>
    
    const int MAXN=1e5+10;
    typedef long long intEx;
    
    struct SuffixArray{
    	int* x;
    	int* y;
    	char s[MAXN];
    	int SA[MAXN];
    	int lg[MAXN];
    	int cnt[MAXN];
    	int rank[MAXN];
    	intEx sum[MAXN];
    	int height[MAXN];
    	int st[18][MAXN];
    	SuffixArray():x(new int[MAXN]),y(new int[MAXN]){}
    	void Build();
    	int LCP(int,int);
    	std::pair<int,int> Query(intEx);
    };
    SuffixArray a,b;
    
    int n;
    int q;
    
    inline intEx Sqr(intEx);
    
    int main(){
    	scanf("%d%d",&n,&q);
    	scanf("%s",a.s+1);
    	for(int i=1;i<=n;i++)
    		b.s[i]=a.s[i];
    	std::reverse(b.s+1,b.s+n+1);
    	a.Build();
    	b.Build();
    	for(int i=0;i<q;i++){
    		intEx x,y;
    		scanf("%lld%lld",&x,&y);
    		auto p1=a.Query(x);
    		auto p2=a.Query(y);
    		if(p1.first==-1||p2.first==-1)
    			puts("-1");
    		else{
    			intEx ans=Sqr(std::min(a.LCP(p1.first,p2.first),std::min(p1.second,p2.second)));
    			p1.first=n-(p1.first+p1.second-1)+1;
    			p2.first=n-(p2.first+p2.second-1)+1;
    			ans+=Sqr(std::min(b.LCP(p1.first,p2.first),std::min(p1.second,p2.second)));
    			printf("%lld
    ",ans);
    		}
    	}
    	return 0;
    }
    
    int SuffixArray::LCP(int x,int y){
    	if(x==y)
    		return INT_MAX;
    	else{
    		int l=rank[x],r=rank[y];
    		if(l>r)
    			std::swap(l,r);
    		++l;
    		int len=r-l+1;
    		return std::min(st[lg[len]][l],st[lg[len]][r-(1<<lg[len])+1]);
    	}
    }
    
    std::pair<int,int> SuffixArray::Query(intEx k){
    	int p=std::lower_bound(sum+1,sum+n+1,k)-sum;
    	if(p==n+1)
    		return {-1,-1};
    	else
    		return {SA[p],n-(sum[p]-k)-SA[p]+1};
    }
    
    void SuffixArray::Build(){
    	int m=127;
    	for(int i=1;i<=n;i++)
    		++cnt[x[i]=s[i]];
    	for(int i=1;i<=m;i++)
    		cnt[i]+=cnt[i-1];
    	for(int i=n;i>=1;i--)
    		SA[cnt[x[i]]--]=i;
    	for(int k=1;k<n;k<<=1){
    		int p=0;
    		for(int i=n-k+1;i<=n;i++)
    			y[++p]=i;
    		for(int i=1;i<=n;i++)
    			if(SA[i]>k)
    				y[++p]=SA[i]-k;
    		memset(cnt+1,0,sizeof(int)*m);
    		for(int i=1;i<=n;i++)
    			++cnt[x[i]];
    		for(int i=1;i<=m;i++)
    			cnt[i]+=cnt[i-1];
    		for(int i=n;i>=1;i--)
    			SA[cnt[x[y[i]]]--]=y[i];
    		std::swap(x,y);
    		x[SA[1]]=1;
    		p=1;
    		for(int i=2;i<=n;i++)
    			x[SA[i]]=(y[SA[i]]==y[SA[i-1]]&&y[SA[i]+k]==y[SA[i-1]+k])?p:++p;
    		if(p>=n)
    			break;
    		m=p;
    	}
    	for(int i=1;i<=n;i++)
    		rank[SA[i]]=i;
    	int k=0;
    	for(int i=1;i<=n;i++){
    		if(rank[i]==1)
    			continue;
    		if(k!=0)
    			--k;
    		int j=SA[rank[i]-1];
    		while(i+k<=n&&j+k<=n&&s[i+k]==s[j+k])
    			++k;
    		height[rank[i]]=k;
    		st[0][rank[i]]=k;
    	}
    	for(int i=1;i<=n;i++)
    		sum[i]=sum[i-1]+(n-SA[i]+1)-height[i];
    	for(int i=1;(1<<i)<=n;i++){
    		++lg[1<<i];
    		for(int j=2;j<=n;j++){
    			st[i][j]=st[i-1][j];
    			if(j+(1<<(i-1))<=n)
    				st[i][j]=std::min(st[i][j],st[i-1][j+(1<<(i-1))]);
    		}
    	}
    	for(int i=1;i<=n;i++)
    		lg[i]+=lg[i-1];
    }
    
    inline intEx Sqr(intEx x){
    	return x*x;
    }
    
    

  • 相关阅读:
    fastadmin的数据限制什么意思?具体是怎么配置的?
    jQuery上传剪切图片的原理和代码
    dedecms模板明明存在,还是报错:说模板不存在
    数据库基本信息查询
    数据库 --- 基础知识 1
    代码块分享
    并发编程知识内容汇总
    网络编程 与 并发编程 汇总
    并发编程 --- 线程补充2
    并发编程 --- 线程补充
  • 原文地址:https://www.cnblogs.com/rvalue/p/10603884.html
Copyright © 2011-2022 走看看