BZOJ4556: [Tjoi2016&Heoi2016]字符串

BZOJ4556: [Tjoi2016&Heoi2016]字符串

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

题解Here!
首先看到最长公共前缀就知道,又是一道后缀数组。。。

设我们的答案为$mid$,那么我们会发现一个很有趣的事实:如果$mid$可行的话,那么任意一个比$mid$小的数也可行。

也就是说,问题满足可二分性,那么我们可以二分答案,将原问题转化为一个判定性问题:$mid$这个答案行不行?

那么我们发现,如果$mid$这个答案可以的话,就会存在一个后缀$S$,

  1. 它的开头在$[a,b-mid+1]$当中。
  2. $lcp(S,c)>=mid$。

再次转化一步,就是询问满足以上两个条件的后缀$S$的个数。

经典的二元限制统计问题。。。

我们的思路很简单,摁死一个再去管下一个,发现一件有趣的事实:

如果把这些后缀排好序,那么$lcp$符合要求的一定是一段连续的区间。

为什么?

因为我们发现排好序以后,$lcp$这个函数是单峰的,并且峰值在自己这里。

那么我们似乎可以二分左端点和右端点,需要$O(1)$求出区间最小值,直接套上$ST$表。

那么最后我们发现现在两个限制都是区间型的了,而且是静态区间,没有修改,所以可以用主席树查询一发。

复杂度是$O(n\log_2^2n)$。

然后就光荣地在$BZOJ$上被卡常了。。。

$UPDATE$:经过某些玄学优化,终于在$8s$内过了。。。

附代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#define MAXN 100010
using namespace std;
int n,m;
int root[MAXN];
char str[MAXN];
int top,sa[MAXN],rk[MAXN],tax[MAXN],tp[MAXN],height[MAXN],f[MAXN][20],Log[MAXN];
inline int read(){
	int date=0,w=1;char c=0;
	while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
	while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
	return date*w;
}
namespace CT{
	int size=0;
	struct Chairman_Tree{
		int l,r,sum;
	}a[MAXN*20];
	inline void buildtree(){
		root[0]=a[0].l=a[0].r=a[0].sum=0;
	}
	void insert(int k,int v,int l,int r,int &rt){
		a[++size]=a[rt];rt=size;
		a[rt].sum+=v;
		if(l==r)return;
		int mid=l+r>>1;
		if(k<=mid)insert(k,v,l,mid,a[rt].l);
		else insert(k,v,mid+1,r,a[rt].r);
	}
	int query(int l,int r,int lside,int rside,int i,int j){
		if(a[i].sum==a[j].sum)return 0;
		if(l<=lside&&rside<=r)return (a[j].sum-a[i].sum);
		int mid=lside+rside>>1,ans=0;
		if(l<=mid)ans+=query(l,r,lside,mid,a[i].l,a[j].l);
		if(mid<r)ans+=query(l,r,mid+1,rside,a[i].r,a[j].r);
		return ans;
	}
}
void radixsort(){
	for(int i=0;i<=top;i++)tax[i]=0;
	for(int i=1;i<=n;i++)tax[rk[i]]++;
	for(int i=1;i<=top;i++)tax[i]+=tax[i-1];
	for(int i=n;i>=1;i--)sa[tax[rk[tp[i]]]--]=tp[i];
}
void suffixsort(){
	top=30;
	for(int i=1;i<=n;i++){
		rk[i]=str[i]-'a'+1;
		tp[i]=i;
	}
	radixsort();
	for(int w=1,p=0;p<n;top=p,w<<=1){
		p=0;
		for(int i=1;i<=w;i++)tp[++p]=n-w+i;
		for(int i=1;i<=n;i++)if(sa[i]>w)tp[++p]=sa[i]-w;
		radixsort();
		swap(tp,rk);
		rk[sa[1]]=p=1;
		for(int i=2;i<=n;i++)
		rk[sa[i]]=(tp[sa[i-1]]==tp[sa[i]]&&tp[sa[i-1]+w]==tp[sa[i]+w])?p:++p;
	}
}
void getheight(){
	for(int i=1,j,k=0;i<=n;i++){
		if(k)k--;
		j=sa[rk[i]-1];
		while(str[i+k]==str[j+k])k++;
		height[rk[i]]=k;
	}
}
void step(){
	Log[0]=0;
	for(int i=1;i<=n;i++){
		f[i][0]=height[i];
		Log[i]=log2(i);
	}
	for(int i=1;i<=Log[n];i++)
	for(int j=1;j+(1<<(i-1))<=n;j++)
	f[j][i]=min(f[j][i-1],f[j+(1<<(i-1))][i-1]);
}
inline int query(int l,int r){
	l++;r++;
	int k=Log[r-l];
	return min(f[l][k],f[r-(1<<k)][k]);
}
bool check(int x,int l1,int r1,int l2,int r2){
	int Left,Right;
	int l=1,r=rk[l2];
	while(l<r){
		int mid=l+r>>1;
		if(query(mid,rk[l2])<x)l=mid+1;
		else r=mid;
	}
	Left=r;
	l=rk[l2];r=n;
	while(l<r){
		int mid=l+r+1>>1;
		if(query(rk[l2],mid)<x)r=mid-1;
		else l=mid;
	}
	Right=r;
	if(CT::query(l1,r1-x+1,1,n,root[Left-1],root[Right]))return true;
	return false;
}
inline int solve(int l1,int r1,int l2,int r2){
	int l=0,r=min(r1-l1+1,r2-l2+1);
	while(l<r){
		int mid=l+r+1>>1;
		if(check(mid,l1,r1,l2,r2))l=mid;
		else r=mid-1;
	}
	return r;
}
void work(){
	int l1,l2,r1,r2;
	while(m--){
		l1=read();r1=read();l2=read();r2=read();
		printf("%d\n",solve(l1,r1,l2,r2));
	}
}
void init(){
	n=read();m=read();
	scanf("%s",str+1);
	suffixsort();
	getheight();
	step();
	CT::buildtree();
	for(int i=1;i<=n;i++){
		root[i]=root[i-1];
		CT::insert(sa[i],1,1,n,root[i]);
	}
}
int main(){
	init();
	work();
    return 0;
}

 


 

据说这题可以用$SAM$搞事?

暂且留个坑吧,等学完$SAM$再来填坑。。。

posted @ 2018-08-27 10:52  符拉迪沃斯托克  阅读(239)  评论(0编辑  收藏  举报
Live2D