BZOJ 4556: [Tjoi2016&Heoi2016]字符串

4556: [Tjoi2016&Heoi2016]字符串

Time Limit: 20 Sec  Memory Limit: 128 MB
Submit: 789  Solved: 306
[Submit][Status][Discuss]

Description

佳媛姐姐过生日的时候,她的小伙伴从某东上买了一个生日礼物。生日礼物放在一个神奇的箱子中。箱子外边写了
一个长为n的字符串s,和m个问题。佳媛姐姐必须正确回答这m个问题,才能打开箱子拿到礼物,升职加薪,出任CE
O,嫁给高富帅,走上人生巅峰。每个问题均有a,b,c,d四个参数,问你子串s[a..b]的所有子串和s[c..d]的最长公
共前缀的长度的最大值是多少?佳媛姐姐并不擅长做这样的问题,所以她向你求助,你该如何帮助她呢?

Input

输入的第一行有两个正整数n,m,分别表示字符串的长度和询问的个数。接下来一行是一个长为n的字符串。接下来
m行,每行有4个数a,b,c,d,表示询问s[a..b]的所有子串和s[c..d]的最长公共前缀的最大值。1<=n,m<=100,000,
字符串中仅有小写英文字母,a<=b,c<=d,1<=a,b,c,d<=n

Output

 对于每一次询问,输出答案。

Sample Input

5 5
aaaaa
1 1 1 5
1 5 1 1
2 3 2 3
2 4 2 3
2 3 2 4

Sample Output

1
1
2
2
2

HINT

Source

分析:

看到$lcp$基本第一想法就是后缀数组...

考虑二分答案,我们二分一个长度,判断是否存在$s[a,b]$的一个子串和$s[c,d]$的$lcp>=len$,判断的时候我们找出在$ran$数组中最大的包含$ran[c]$的区间,使得这一段区间的$height$最小值$>=len$,然后判断是否存在一个后缀的下标是区间$[a,b+1-len]$中的,因为要判断一个区间内部的元素存在性问题,所以我们用主席树维护下标存在与否,也就是主席树的下标代表的是后缀的开头位置,如果存在就设为$1$...

还是很好写的...

代码:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
//by NeighThorn
using namespace std;

const int maxn=100000+5,maxm=7000000+5;

int n,m,tot,sa[maxn],gs[maxn],wb[maxn],wv[maxn],ran[maxn],height[maxn];
int st[maxn][25],root[maxn],ls[maxm],rs[maxm],sum[maxm];

char s[maxn];

inline bool cmp(int *x,int a,int b,int l){
	return x[a]==x[b]&&x[a+l]==x[b+l];
}

inline void da(int *sa,int *x,int n,int m){
	int i,j,p,*y=wb;
	for(i=0;i<m;i++) gs[i]=0;
	for(i=0;i<n;i++) gs[x[i]]++;
	for(i=1;i<m;i++) gs[i]+=gs[i-1];
	for(i=n-1;~i;i--) sa[--gs[x[i]]]=i;
	for(j=1,p=1;p<n;j<<=1,m=p){
		for(i=n-j,p=0;i<n;i++) y[p++]=i;
		for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j;
		for(i=0;i<n;i++) wv[i]=x[y[i]];
		for(i=0;i<m;i++) gs[i]=0;
		for(i=0;i<n;i++) gs[wv[i]]++;
		for(i=1;i<m;i++) gs[i]+=gs[i-1];
		for(i=n-1;~i;i--) sa[--gs[wv[i]]]=y[i];
		p=1;swap(x,y);x[sa[0]]=0;
		for(i=1;i<n;i++) x[sa[i]]=cmp(y,sa[i],sa[i-1],j)?p-1:p++;
	}
}

inline void calcheight(int n){
	int i,j,k=0;
	for(i=0;i<=n;i++) ran[sa[i]]=i;
	for(i=0;i<n;height[ran[i++]]=k)
		for(k?k--:233,j=sa[ran[i]-1];s[i+k]==s[j+k];k++);
}

inline bool cmp1(int x,int y){
	return height[x]<height[y];
}

inline void prework(void){
	for(int i=1;i<=n;i++) st[i][0]=i;
	for(int j=1;j<=20;j++)
		for(int i=1;i+(1<<j-1)<=n;i++)
			st[i][j]=min(st[i+(1<<j-1)][j-1],st[i][j-1],cmp1);
}

inline int query(int x,int y){
	if(x>y) return n;
	int d=y-x,k;
	for(k=20;k>=0;k--)
		if(d>>k||k==0)
			break;
	return height[min(st[x][k],st[y-(1<<k)+1][k],cmp1)];
}

inline void change(int l,int r,int x,int &y,int pos){
	y=++tot;sum[y]=sum[x]+1;
	if(l==r) return;
	int mid=(l+r)>>1;ls[y]=ls[x],rs[y]=rs[x];
	if(pos<=mid)
		change(l,mid,ls[x],ls[y],pos);
	else
		change(mid+1,r,rs[x],rs[y],pos);
}

inline int query(int l,int r,int x,int y,int L,int R){
	if(sum[y]-sum[x]==0) return 0;
	if(l==L&&R==r) return sum[y]-sum[x]>0;
	int mid=(l+r)>>1;
	if(R<=mid)
		return query(l,mid,ls[x],ls[y],L,R);
	else if(L>mid)
		return query(mid+1,r,rs[x],rs[y],L,R);
	else
		return query(l,mid,ls[x],ls[y],L,mid)|query(mid+1,r,rs[x],rs[y],mid+1,R);
}

inline bool check(int a,int b,int c,int len){
	int l=1,r=ran[c],up=ran[c],down=ran[c];
	while(l<=r){
		int mid=(l+r)>>1;
		if(query(mid+1,ran[c])>=len)
			up=mid,r=mid-1;
		else l=mid+1;
	}
	l=ran[c];r=n;
	while(l<=r){
		int mid=(l+r)>>1;
		if(query(ran[c]+1,mid)>=len)
			down=mid,l=mid+1;
		else r=mid-1;
	}
	if(query(0,n-1,root[up-1],root[down],a,b+1-len)) return true;
	return false;
}

inline int solve(int a,int b,int c,int d){
	int l=1,r=min(d-c+1,b-a+1),ans=0;
	while(l<=r){
		int mid=(l+r)>>1;
		if(check(a,b,c,mid)) ans=mid,l=mid+1;
		else r=mid-1;
	}
	return ans;
}

signed main(void){
	scanf("%d%d%s",&n,&m,s);
	for(int i=0;i<n;i++) ran[i]=(int)s[i];
	da(sa,ran,n+1,233);calcheight(n);prework();
	for(int i=1;i<=n;i++)
		change(0,n-1,root[i-1],root[i],sa[i]);
	for(int i=1,a,b,c,d;i<=m;i++){
		scanf("%d%d%d%d",&a,&b,&c,&d);
		printf("%d\n",solve(a-1,b-1,c-1,d-1));
	}
	return 0;
}

  


By NeighThorn

 
posted @ 2017-03-30 19:50  NeighThorn  阅读(266)  评论(0编辑  收藏  举报