首页 > 代码库 > [bzoj4556] [Tjoi2016&Heoi2016]字符串

[bzoj4556] [Tjoi2016&Heoi2016]字符串

翻转原串,建后缀自动机.

然后先考虑最朴素的思路,找到d所对应的节点,然后一直往上走,并更新答案.

发现由于有a,b的限制,更新答案需要取min,很不爽,不如二分答案.

然后就可以转化为判定性问题,用字符串定位技术找到当前的cd对应的字符串(其实就是倍增+len判定),

判定当前的节点是否有当前a,b范围的right集.

要知道每个节点的right集具体是什么用dfs序建立主席树即可.

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<ctime>
#include<string>
#include<iomanip>
#include<algorithm>
#include<map>
using namespace std;
#define LL long long
#define FILE "dealing"
#define up(i,j,n) for(int i=j;i<=n;++i)
#define db double
#define ull unsigned long long
#define eps 1e-10
#define pii pair<int,int>
int read(){
	int x=0,f=1,ch=getchar();
	while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();}
	while(ch>=‘0‘&&ch<=‘9‘){x=(x<<1)+(x<<3)+ch-‘0‘;ch=getchar();}
	return f*x;
}
const int maxn=200200,mod=(int)(1e9+7+0.1),limit=(int)(1e6+1);
int n,m;
char s[maxn>>1];
struct node{
	int y,next;
}e[maxn<<1];
int len,linkk[maxn],pre[maxn],dfs_clock=0,low[maxn],r[maxn],dep[maxn],fa[maxn][26],id[maxn],zuo[maxn],val[maxn];
void insert(int x,int y){
	e[++len].y=y;
	e[len].next=linkk[x];
	linkk[x]=len;
}
namespace Sam{
	int np,p,q,nq,pre[maxn],c[maxn][26],len[maxn],cnt,now,count[maxn],sa[maxn];
	int extend(int x){
		p=now;now=np=++cnt;len[np]=len[p]+1;
		while(p&&!c[p][x])c[p][x]=np,p=pre[p];
		if(!p)pre[np]=1;
		else {
			q=c[p][x];
			if(len[q]==len[p]+1)pre[np]=q;
			else {
				len[nq=++cnt]=len[p]+1;
				memcpy(c[nq],c[q],sizeof(c[q]));
				pre[nq]=pre[q];
				pre[q]=pre[np]=nq;
				while(p&&c[p][x]==q)c[p][x]=nq,p=pre[p];
			}
		}
		return np;
	}
	void build(){
		now=1,cnt=1;
		up(i,1,n)val[extend(s[i]-‘a‘)]=i;
		up(i,1,cnt)zuo[val[i]]=i;
	}
	void prepare(){
		up(i,1,cnt)count[len[i]]++;
		up(i,1,cnt)count[i]+=count[i-1];
		for(int i=cnt;i>=1;i--)sa[count[len[i]]--]=i;
		for(int i=cnt;i>=2;i--)insert(pre[sa[i]],sa[i]),insert(sa[i],pre[sa[i]]);
	}
};
namespace chair_man_tree{
	const int maxn=3000000;
	int c[maxn][2],sum[maxn],root[maxn],n,cnt=0,L,R;
	void updata(int x){sum[x]=sum[c[x][0]]+sum[c[x][1]];}
	void insert(int x,int& o,int key,int l,int r){
		if(key==0){o=x;return;}
		o=++cnt;
		if(l==r){
			sum[o]=sum[x]+1;
			return;
		}
		int mid=(l+r)>>1;
		if(key>mid){
			c[o][0]=c[x][0];
			insert(c[x][1],c[o][1],key,mid+1,r);
		}
		else {
			c[o][1]=c[x][1];
			insert(c[x][0],c[o][0],key,l,mid);
		}
		updata(o);
	}
	void build(){
		up(i,1,n)insert(root[i-1],root[i],r[i],1,n);
	}
	int query(int x,int o,int l,int r){
		//printf("%d %d %d\n",l,r,sum[o]-sum[x]);
		if(l>R||r<L)return 0;
		if(l>=L&&r<=R)return sum[o]-sum[x];
		if(!(sum[o]-sum[x]))return 0;
		int mid=(l+r)>>1;
		return query(c[x][0],c[o][0],l,mid)+query(c[x][1],c[o][1],mid+1,r);
	}
	int Query(int l,int r,int Le,int Ri){
		L=Le,R=Ri;
		//cout<<L<<" "<<R<<endl;
		return query(root[l],root[r],1,n);
	}
};
void dfs(int x){
	pre[x]=++dfs_clock;
	//printf("%d %d %d %d\n",x,pre[x],val[x],fa[x][0]);
	for(int i=linkk[x];i;i=e[i].next){
		if(e[i].y==fa[x][0])continue;
		dep[e[i].y]=dep[x]+1;
		fa[e[i].y][0]=x;
		dfs(e[i].y);
	}
	low[x]=dfs_clock;
}
bool check(int a,int b,int c,int d){//返回c,d位置是否有right集有a,b的元素
	int y=zuo[d];
	for(int i=25;i>=0;i--)
		if(Sam::len[fa[y][i]]>=c)y=fa[y][i];//定位c,d字符串
	int sum=chair_man_tree::Query(pre[y]-1,low[y],a,b);
	if(sum)return 1;
	else return 0;
}
int main(){
	freopen(FILE".in","r",stdin);
	freopen(FILE".out","w",stdout);
	n=read(),m=read();
	scanf("%s",s+1);
	reverse(s+1,s+n+1);
	Sam::build();
	Sam::prepare();
	dfs(1);
	up(i,1,Sam::cnt)id[pre[i]]=i;//主席树对应树
	up(i,1,Sam::cnt)r[i]=val[id[i]];
	chair_man_tree::n=Sam::cnt;
	chair_man_tree::build();
	for(int j=1;j<=25;j++)
		for(int i=1;i<=Sam::cnt;i++)
			fa[i][j]=fa[fa[i][j-1]][j-1];
	while(m--){
		int a=n-read()+1,b=n-read()+1,c=n-read()+1,d=n-read()+1;
		swap(a,b);swap(c,d);
		int left=1,right=min(d-c+1,b-a+1),ans=0;
		while(left<=right){
			int mid=(left+right)>>1;
			if(check(a+mid-1,b,mid,d))left=mid+1,ans=mid;
			else right=mid-1;
		}
		printf("%d\n",ans);
	}
	return 0;
}

  

[bzoj4556] [Tjoi2016&Heoi2016]字符串