[HEOI2016/TJOI2016]字符串

字符串

题解

挺好想的一道题。

很明显,对于一个询问,我们是可以去二分最长公共前缀的最大值的。
当长度 l l l满足是,长度 l − 1 l-1 l1显然也满足条件,是具有单调性的。

至于如何判断长度 m i d mid mid合不合法,考虑SA。
显然, m i d mid mid合法时, c c c显然与 [ a , a − m i d + 1 ] [a,a-mid+1] [a,amid+1]之间某个位置有不低于 m i d mid midlcp
因为对于排序后的SA数组,相邻相邻两个字符串的lcp最大,向外逐渐减小。
所以我们可以先将SA建出来,排完序后,与 c c c有长度不低于 m i d mid midlcp的串一定是一个包含串 c c c连续的区间。

我们可以去二分这个区间的左右端点,看这个区间中有没有 [ a , a − m i d + 1 ] [a,a-mid+1] [a,amid+1]开始的字符串即可。
因为左右端点二分时需要求中间一段区间 h e i g h t height height数组的最小值,而采用线段树的话会达到3个 l o g log log,我们只能采用st表去维护 h e i g h t height height数组,以做到 O ( 1 ) O(1) O(1)查询。
至于区间中寻找有没有 [ a , a − m i d + 1 ] [a,a-mid+1] [a,amid+1]开头的字符串,可以使用主席树,也是很常规的操作。

时间复杂度 O ( n l o g 2 n ) O\left(nlog^2n\right) O(nlog2n)

源码

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
using namespace std;
#define MAXN 3000005
#define lowbit(x) (x&-x)
#define reg register
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<int,int> pii;
const int INF=0x7f7f7f7f;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
int n,m,q,x[MAXN],y[MAXN],c[MAXN],z[MAXN],lg[MAXN];
int sa[MAXN],rk[MAXN],hei[MAXN],st[25][MAXN];char str[MAXN];
void getSa(){
	for(int i=1;i<=n;i++)c[x[i]=str[i]]++;
	for(int i=2;i<=m;i++)c[i]+=c[i-1];
	for(int i=n;i>0;i--)sa[c[x[i]]--]=i;
	for(int k=1;k<=n;k<<=1){
		int 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;
		for(int i=1;i<=m;i++)c[i]=0;
		for(int i=1;i<=n;i++)c[x[i]]++,z[i]=x[i];
		for(int i=2;i<=m;i++)c[i]+=c[i-1];
		for(int i=n;i>0;i--)sa[c[x[y[i]]]--]=y[i];
		for(int i=1;i<=n;i++)x[i]=y[i]=0;x[sa[1]]=1;num=1;
		for(int i=2;i<=n;i++)x[sa[i]]=(z[sa[i]]==z[sa[i-1]]&&z[sa[i]+k]==z[sa[i-1]+k])?num:++num;
		if(num==n)break;m=num;
	}
}
void getHi(){
	int k=0;for(int i=1;i<=n;i++)rk[sa[i]]=i;
	for(int i=1;i<=n;i++){
		if(k)k--;int j=sa[rk[i]-1];
		while(str[i+k]==str[j+k])k++;hei[rk[i]]=k;
	}
}
void getSt(){
	for(int j=2;j<=n;j++)lg[j]=lg[j>>1]+1;
	for(int i=0;i<n;i++)st[0][i]=hei[i+1];
	for(int i=1;i<=lg[n];i++)
		for(int j=0;j<n-(1<<i-1);j++)
			st[i][j]=min(st[i-1][j],st[i-1][j+(1<<i-1)]);
}
int rmq(int l,int r){return min(st[lg[r-l]][l],st[lg[r-l]][r-(1<<lg[r-l])]);}
struct ming{int val,lson,rson;};
class Tree{
	private:
		ming tr[MAXN*40];int tot,root[MAXN];
		void insert(int las,int &now,int l,int r,int ai){
			tr[now=++tot]=tr[las];tr[now].val++;if(l==r)return;int mid=l+r>>1;
			if(ai<=mid)insert(tr[las].lson,tr[now].lson,l,mid,ai);
			else insert(tr[las].rson,tr[now].rson,mid+1,r,ai);
		}
		int query(int las,int now,int l,int r,int al,int ar){
			if(l>r||l>ar||r<al)return 0;
			if(al<=l&&r<=ar)return tr[now].val-tr[las].val;int mid=l+r>>1,res=0;
			if(al<=mid)res+=query(tr[las].lson,tr[now].lson,l,mid,al,ar);
			if(ar>mid)res+=query(tr[las].rson,tr[now].rson,mid+1,r,al,ar);
			return res;
		}
	public:
		void build(){root[0]=++tot;for(int i=1;i<=n;i++)insert(root[i-1],root[i],1,n,sa[i]);}
		int ask(int l,int r,int al,int ar){return query(root[l-1],root[r],1,n,al,ar);}
}T;
bool check(int a,int b,int c,int x){
	int l=1,r=rk[c],al,ar;while(l<r){int mid=l+r>>1;if(rmq(mid,rk[c])<x)l=mid+1;else r=mid;}
	al=l;l=rk[c],r=n;while(l<r){int mid=l+r+1>>1;if(rmq(rk[c],mid)<x)r=mid-1;else l=mid;}ar=r;
	return T.ask(al,ar,a,b)>0;
}
signed main(){
	read(n);read(q);scanf("%s",str+1);m=126;
	getSa();getHi();getSt();T.build();
	for(int i=1;i<=q;i++){
		int a,b,c,d;read(a);read(b);read(c);read(d);
		int l=0,r=min(b-a+1,d-c+1);
		while(l<r){
			int mid=l+r+1>>1;
			if(check(a,b-mid+1,c,mid))l=mid;
			else r=mid-1;
		}
		printf("%d\n",l);
	}
	return 0;
}

谢谢!!!

posted @ 2021-02-20 19:24  StaroForgin  阅读(13)  评论(0)    收藏  举报  来源