【BZOJ】4556: [Tjoi2016&Heoi2016]字符串-SA

版权声明:欢迎转载(请附带原链接)ヾ(๑╹◡╹)ノ" https://blog.csdn.net/corsica6/article/details/84887041

传送门:bzoj4556


题解

L C P LCP 自然地联想到SA。

首先二分枚举答案 m i d mid ,在 s a sa 数组上必然是连续的一段满足于 r k c rk_c l c p m i d lcp\geq mid ,假设这段区间为 [ L , R ] [L,R] (可以二分找出/或者直接倍增)。

判断是否答案存在就是 [ L , R ] [L,R] 中是否存在 r k a , r k a + 1 , . . , r k b m i d + 1 rk_{a},rk_{a+1},..,rk_{b-mid+1} 中的任意一个。

那么考虑主席树按 s a i sa_i 逐个加入即可判断。

复杂度 O ( n l o g 2 n ) O(nlog^2n)


代码

#include<bits/stdc++.h>
#define mid ((l+r)>>1)
#define lc k<<1
#define rc k<<1|1
using namespace std;
const int N=1e5+10;

int n,m,rk[N],bin[30],lg[N],ans;
int sa[N],t1[N],t2[N],c[N];
int rt[N],ss[N*25],ls[N*25],rs[N*25],cnt;
int d[20][N];char s[N];

inline void build()
{
	int i,k,p,m=26,*x=t1,*y=t2;
	for(i=1;i<=n;++i) c[(x[i]=s[i]-'a'+1)]++;
	for(i=2;i<=m;++i) c[i]+=c[i-1];
	for(i=n;i;--i) sa[c[x[i]]--]=i;
	for(k=1;k<n;k<<=1){
		for(p=0,i=n-k+1;i<=n;++i) y[++p]=i;
		for(i=1;i<=n;++i) if(sa[i]>k) y[++p]=sa[i]-k;
		for(i=1;i<=m;++i) c[i]=0;
		for(i=1;i<=n;++i) c[x[y[i]]]++;
		for(i=2;i<=m;++i) c[i]+=c[i-1];
		for(i=n;i;--i) sa[c[x[y[i]]]--]=y[i];
		p=1;swap(x,y);x[sa[1]]=1;
		for(i=2;i<=n;++i){
			p+=((y[sa[i]]!=y[sa[i-1]])||(y[sa[i]+k]!=y[sa[i-1]+k]));
			x[sa[i]]=p;
		}
		if(p>=n) break;
		m=p;
	}
	for(i=1;i<=n;++i) rk[sa[i]]=i;
}

inline void cal()
{
	int i,j,pr=0;
	for(i=1;i<=n;++i){
		if(rk[i]==1) {pr=0;continue;}
		for(pr=max(0,pr-1),j=sa[rk[i]-1];i+pr<=n && j+pr<=n && s[i+pr]==s[j+pr];++pr);
		d[0][rk[i]]=pr;
	}
	for(i=1;bin[i]<=n;++i)
	  for(j=1;j+bin[i]-1<=n;++j)
	    d[i][j]=min(d[i-1][j],d[i-1][j+bin[i-1]]);
}

inline int lcp(int x,int y)
{
    x++;int bs=lg[y-x+1];
	return min(d[bs][x],d[bs][y-bin[bs]+1]);
}

void ins(int pre,int &k,int l,int r,int pos)
{
	if(k==pre){k=++cnt;ls[k]=ls[pre];rs[k]=rs[pre];ss[k]=ss[pre];}
    ss[k]++;if(l^r){
    	if(pos<=mid) ins(ls[pre],ls[k],l,mid,pos);
    	else ins(rs[pre],rs[k],mid+1,r,pos);
    }
}

int ask(int pre,int k,int l,int r,int L,int R)
{
	if((!k)||(ss[k]==ss[pre])) return 0;
	if(L<=l && r<=R) return ss[k]-ss[pre];
	if(L<=mid && ask(ls[pre],ls[k],l,mid,L,R)) return 1;
	if(R>mid) return ask(rs[pre],rs[k],mid+1,r,L,R);
	return 0;
}

inline int gt(int x,int op,int lim)
{
	int l,r,re=x;
	if(!op){
		l=1;r=x-1;
		for(;l<=r;) lcp(mid,x)>=lim?r=(re=mid)-1:l=mid+1;
	}else{
		l=x+1;r=n;
		for(;l<=r;) lcp(x,mid)>=lim?l=(re=mid)+1:r=mid-1;
	}
	return re;
}

void sol()
{
	int a,b,c,d,l=1,r,L,R,x;ans=0;
	scanf("%d%d%d%d",&a,&b,&c,&d);
	r=min(d-c,b-a)+1;c=rk[c];
	for(;l<=r;){
		x=mid;L=gt(c,0,x);R=gt(c,1,x);
		if(ask(rt[L-1],rt[R],1,n,a,b-x+1)) l=(ans=x)+1;
		else r=x-1;
	}
	printf("%d\n",ans);
}

int main(){
	int i,j;bin[0]=1;
	scanf("%d%d%s",&n,&m,s+1);
	for(i=1;i<30;++i) bin[i]=bin[i-1]<<1;
	for(i=2;i<N;++i) lg[i]=lg[i>>1]+1;
	build();cal();
	for(i=1;i<=n;++i) rt[i]=rt[i-1],ins(rt[i-1],rt[i],1,n,sa[i]);
	for(;m;--m) sol();
	return 0;
}

猜你喜欢

转载自blog.csdn.net/corsica6/article/details/84887041