zoukankan      html  css  js  c++  java
  • BZOJ3230 相似子串 字符串 SA ST表

    原文链接http://www.cnblogs.com/zhouzhendong/p/9033092.html

    题目传送门 - BZOJ3230

    题意

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

      现在有$Q$组询问,每组询问内容如下:

      两个正整数$i,j$。

      设$s_i,s_j$分别表示$s$的所有本质不同的子串中字典序第$i$小和第$j$小的子串。

      请你输出$|lcp(s_i,s_j)|^2+|lcs(s_i,s_j)|^2$。

      如果不存在$s_i$或者$s_j$,则输出$-1$。

      $n,Qleq 10^5$

    题解

      这题大概是我做的前几题$SA$的大整合+升级版吧。

      我们考虑如何找到本质不同子串中第$k$大的子串。

      我们考虑按照串$s$的后缀大小从小到大处理。

      对于排名为$i$的后缀$SA[i]$,考虑它 除了与排名更靠前的其他后缀 的相同前缀以外的前缀所代表的子串。

      很显然,这些子串都是本质不同的。根据后缀数组的性质,也很显然,这些串都不同于之前已经计算过的串。不然的话,$height[i]$会更大。

      而且,这些子串是按照字典序排的。

      现在,我们发现这些后,不需要处理出所有子串。

      统计原串后缀排名前$i$的后缀所包含的子串的个数,记为$presum[i]$。

      显然,$presum[i]=presum[i-1]+len(SA[i])-height[i]$。

      于是我们在查找第$k$大子串的时候就可以通过二分或者倍增来快速地找到第$k$大的子串所在的后缀,然后确定第$k$大的子串就容易了。

      至于求两个子串的$LCP$和$LCS$长度,是后缀数组的经典操作,这里就不加赘述了。

    代码

    #include <bits/stdc++.h>
    #define rank r_a_n_k
    using namespace std;
    typedef long long LL;
    const int N=200005;
    int n,Q;
    int SA[N],rank[N],height[N],tax[N],tmp[N];
    int sSA[N],srank[N],sheight[N];
    int ST[N][19];
    LL presum[N];
    char s[N];
    void Sort(int n,int m,int SA[],int rank[]){
    	for (int i=0;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,int SA[],int rank[],int height[]){
    	memset(SA,0,sizeof SA);
    	memset(tmp,0,sizeof tmp);
    	memset(rank,0,sizeof rank);
    	memset(height,0,sizeof height);
    	for (int i=1;i<=n;i++)
    		rank[i]=s[i],tmp[i]=i;
    	int m=127;
    	Sort(n,m,SA,rank);
    	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,SA,rank);
    		for (int i=1;i<=n;i++)
    			swap(rank[i],tmp[i]);
    		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++);
    	height[1]=0;
    }
    void Get_ST(int n){
    	memset(ST,0,sizeof ST);
    	for (int i=1;i<=n;i++){
    		ST[i][0]=height[i];
    		for (int j=1;j<19;j++){
    			ST[i][j]=ST[i][j-1];
    			if (i-(1<<(j-1))>0)
    				ST[i][j]=min(ST[i][j],ST[i-(1<<(j-1))][j-1]);
    		}
    	}
    }
    int Query(int L,int R){
    	int val=floor(log(R-L+1)/log(2));
    	return min(ST[L+(1<<val)-1][val],ST[R][val]);
    }
    int LCP(int x,int y){
    	if (x==y)
    		return n;
    	x=rank[x],y=rank[y];
    	return Query(min(x,y)+1,max(x,y));
    }
    int LCS(int x,int y){
    	return LCP(n*2+2-x,n*2+2-y);
    }
    void GetSubstr(LL k,int &L,int &R){
    	int pos,i;
    	for (pos=0,i=19;i>=0;i--)
    		if (pos+(1<<i)<n&&presum[pos+(1<<i)]<k)
    			pos+=(1<<i);
    	L=sSA[pos+1];
    	R=1LL*L+k-presum[pos]+sheight[pos+1]-1;
    }
    int main(){
    	scanf("%d%d",&n,&Q);
    	scanf("%s",s+1);
    	s[n+1]='#';
    	for (int i=n*2+1;i>n+1;i--)
    		s[i]=s[n*2+2-i];
    	Suffix_Array(s,n*2+1,SA,rank,height);
    	Get_ST(n*2+1);
    	for (int i=n+1;i<=n*2+1;i++)
    		s[i]=0;
    	Suffix_Array(s,n,sSA,srank,sheight);
    	presum[0]=0;
    	for (int i=1;i<=n;i++)
    		presum[i]=presum[i-1]+(n-sSA[i]+1)-sheight[i];
    	while (Q--){
    		LL k1,k2;
    		int L1,R1,L2,R2;
    		scanf("%lld%lld",&k1,&k2);
    		if (k1>presum[n]||k2>presum[n]){
    			puts("-1");
    			continue;
    		}
    		GetSubstr(k1,L1,R1);
    		GetSubstr(k2,L2,R2);
    		LL len=min(R1-L1+1,R2-L2+1);
    		LL lcp=min(len,(LL)LCP(L1,L2)),lcs=min(len,(LL)LCS(R1,R2));
    		printf("%lld
    ",lcp*lcp+lcs*lcs);
    	}
    	return 0;
    }
    

      

  • 相关阅读:
    步步为营 SharePoint 开发学习笔记系列总结
    Type 关键字解读
    C# 利用反射方便取得DbDataReader里的值
    WCF 开发学习笔记
    用状态模式实现状态机工作流
    步步为营UML建模系列总结
    策略模式实现支持多种类数据库的DBHelp
    步步为营 .NET 设计模式学习笔记系列总结
    BPEL 语言介绍和应用
    步步为营 .NET 代码重构学习笔记系列总结
  • 原文地址:https://www.cnblogs.com/zhouzhendong/p/BZOJ3230.html
Copyright © 2011-2022 走看看