• BZOJ 3230: 相似子串(后缀数组)


    传送门

    解题思路

      其实题目挺好想的。首先子串排名可以由后缀数组求得,因为不算重复的,所以后缀数组的每个后缀排名的去掉(lcp)的前缀排名为当前后缀的子串排名。这样就可以预处理出每个后缀的(l,r),查询的时候二分出来属于哪个后缀,用(rmq)求个(lcp)。倒过来处理的式子比较麻烦,要先将排名转化成位置,然后找到对应的倒过来的位置,最后在转化为排名,具体看代码。

    代码

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    
    using namespace std;
    const int MAXN = 100005;
    typedef long long LL;
    
    inline LL rd(){
    	LL x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)) f=ch=='-'?0:1,ch=getchar();
    	while(isdigit(ch)) x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
    	return f?x:-x;
    }	
    
    int n,q,m;
    LL l[MAXN],r[MAXN];
    
    struct SA{
    	int x[MAXN<<1],y[MAXN<<1],c[MAXN],sa[MAXN],rk[MAXN];
    	int num,height[MAXN],Min[MAXN][20];
    	char s[MAXN];
    	inline void get_SA(){m='z';
    		for(int i=1;i<=n;i++) x[i]=s[i],c[x[i]]++;
    		for(int i=2;i<=m;i++) c[i]+=c[i-1];
    		for(int i=n;i;i--) sa[c[x[i]]--]=i;
    		for(int k=1;k<=n;k<<=1){num=0;
    			for(int i=n-k+1;i<=n;i++) y[++num]=i;
    			for(int i=1;i<=n;i++) if(sa[i]>k) y[++num]=sa[i]-k;
    			memset(c,0,sizeof(c));
    			for(int i=1;i<=n;i++) c[x[i]]++;
    			for(int i=2;i<=m;i++) c[i]+=c[i-1];
    			for(int i=n;i;i--) sa[c[x[y[i]]]--]=y[i],y[i]=0;
    			swap(x,y);x[sa[1]]=1;num=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])?num:++num;
    			m=num;if(n==m) break; 
    		}
    	}
    	inline void get_height(){
    		for(int i=1;i<=n;i++) rk[sa[i]]=i;int k=0,j;
    		for(int i=1;i<=n;i++){
    			if(rk[i]==1) continue;
    			if(k) k--;j=sa[rk[i]-1];
    			while(i+k<=n && j+k<=n && s[i+k]==s[j+k]) k++;
    			height[rk[i]]=k;
    		}
    	}
    	inline void build(){
    		for(int i=1;i<=n;i++) Min[i][0]=height[i];
    		for(int j=1;(1<<j)<=n;j++)
    			for(int i=1;i+(1<<j)-1<=n;i++)
    				Min[i][j]=min(Min[i][j-1],Min[i+(1<<(j-1))][j-1]);
    	}
    	inline int query(int x,int y){
    		if(x==y) return n+1;
    		if(x>y) swap(x,y);x++;int t=log2(y-x+1);
    		return min(Min[x][t],Min[y-(1<<t)+1][t]); 
    	}
    	inline void prework(){
    		get_SA();get_height();build();
    	}
    }A,B;
    
    inline int check(LL lim){
    	int L=1,R=n,mid;
    	while(L<=R){
    		mid=(L+R)>>1;
    		if(l[mid]<=lim && r[mid]>=lim) return mid;
    		if(l[mid]>lim) R=mid-1;
    		else L=mid+1;
    	}
    }
    
    int main(){
    	int posA,posB,lenA,lenB,L,R;LL x,y;
    	n=rd(),q=rd();scanf("%s",A.s+1);
    	for(int i=1;i<=n;i++) B.s[n-i+1]=A.s[i];
    	A.prework();B.prework();
    	for(int i=1;i<=n;i++)
    		l[i]=r[i-1]+1,r[i]=l[i]+n-A.sa[i]-A.height[i];
    //	for(int i=1;i<=n;i++) cout<<l[i]<<" "<<r[i]<<endl;
    	while(q--){
    		x=rd(),y=rd();
    		if(x>r[n] || y>r[n]) {puts("-1");continue;}
    		posA=check(x);posB=check(y);
    //		cout<<posA<<" "<<posB<<endl;
    		lenA=A.height[posA]+x-l[posA]+1;
    		lenB=A.height[posB]+y-l[posB]+1;
    //		cout<<lenA<<" "<<lenB<<endl;
    		L=min(min(lenA,lenB),A.query(posA,posB));
    		R=min(min(lenA,lenB),B.query(B.rk[n-(A.sa[posA]+lenA-1)+1],B.rk[n-(A.sa[posB]+lenB-1)+1]));
    //		cout<<L<<" "<<R<<endl;
    		printf("%lld
    ",(LL)L*L+(LL)R*R);
    	}
    	return 0;
    }
    
  • 相关阅读:
    复制出一个同样的窗体
    DBEdit的OnChange事件(100分)
    delphi中DBEdit,数据的修改和提交
    delphi 根据DELTA自动生成SQL语句
    clientdataset生成sql
    在delphi中生成GUID/自动获取临时表名......
    如何得到表的主键名称(字段名称)
    通用的更新方法 THlsImplBase
    Delphi中ClientDataSet的用法小结
    ORACLE 字符串补零
  • 原文地址:https://www.cnblogs.com/sdfzsyq/p/10185161.html
Copyright © 2020-2023  润新知