字符串游戏 - 主席树 - 后缀数组

题目大意:给你个字符串,若干次询问本质不同的子串中第k1小中第k2次出现的子串,或者S[l,r]这个子串在S中的排名(本质不同的排名)以及在相同的子串中是地几个出现的。
题解:后缀数组后第一问可以预处理倍增定位然后主席树;第二问可以直接线段树。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<assert.h>
#define N 500010
#define LOG 21
#define lint long long
#define gc getchar()
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
inline int inn()
{
	int x,ch;while((ch=gc)<'0'||ch>'9');
	x=ch^'0';while((ch=gc)>='0'&&ch<='9')
		x=(x<<1)+(x<<3)+(ch^'0');return x;
}
inline lint inln()
{
	lint x,ch;while((ch=gc)<'0'||ch>'9');
	x=ch^'0';while((ch=gc)>='0'&&ch<='9')
		x=(x<<1)+(x<<3)+(ch^'0');return x;
}
int wa[N<<1],wb[N<<1],cnt[N],v[N],sa[N],rk[N],h[N],a[N];
char s[N];lint ps[N];int mnv[N][LOG],Log[N];
inline int cmp(int *a,int x,int y,int l) { return a[x]==a[y]&&a[x+l]==a[y+l]; }
int getSA(int *a,int n,int m)
{
	int *x=wa,*y=wb,i,j,p;
	memset(wa,0,sizeof(wa));
	memset(wb,0,sizeof(wb));
	for(i=1;i<=m;i++) cnt[i]=0;
	for(i=1;i<=n;i++) cnt[x[i]=a[i]]++;
	for(i=1;i<=m;i++) cnt[i]+=cnt[i-1];
	for(i=n;i;i--) sa[cnt[x[i]]--]=i;
	for(p=0,j=1;p<=n;j<<=1,m=p)
	{
		for(p=0,i=n-j+1;i<=n;i++) y[++p]=i;
		for(i=1;i<=n;i++)
			if(sa[i]>j) y[++p]=sa[i]-j;
		for(i=1;i<=m;i++) cnt[i]=0;
		for(i=1;i<=n;i++) cnt[v[i]=x[y[i]]]++;
		for(i=1;i<=m;i++) cnt[i]+=cnt[i-1];
		for(i=n;i;i--) sa[cnt[v[i]]--]=y[i];
		for(swap(x,y),p=i=1;i<=n;i++)
			x[sa[i]]=(cmp(y,sa[i],sa[i-1],j)?p-1:(p++));
	}
	for(i=1;i<=n;i++) rk[sa[i]]=i;
	for(i=1,p=0;i<=n;h[rk[i++]]=p)
		for((p?p--:0),j=sa[rk[i]-1];a[i+p]==a[j+p];p++);
	return 0;
}
inline int LCP(int x,int y)
{
	assert(x!=y);if(x>y) swap(x,y);x++;int k=Log[y-x+1];
	return min(mnv[x][k],mnv[y-(1<<k)+1][k]);
}
inline int get_s(int x,int len)
{
	int L=1,R=x-1,mid=(L+R)>>1;
	while(L<=R)
	{
		if(LCP(mid,x)>=len) R=mid-1;
		else L=mid+1;mid=(L+R)>>1;
	}
	return L;
}
inline int get_t(int x,int len,int n)
{
	int L=x+1,R=n,mid=(L+R)>>1;
	while(L<=R)
	{
		if(LCP(x,mid)>=len) L=mid+1;
		else R=mid-1;mid=(L+R)>>1;
	}
	return R;
}
struct segment{ int s;segment *ch[2]; }*T[N];
inline int push_up(segment* &rt) { return rt->s=rt->ch[0]->s+rt->ch[1]->s; }
int build(segment* &rt,int l,int r)
{
	rt=new segment,rt->s=0;if(l==r) return 0;int mid=(l+r)>>1;
	return build(rt->ch[0],l,mid),build(rt->ch[1],mid+1,r),0;
}
int build(segment* &now,segment* &pre,int p,int l,int r)
{
	int mid=(l+r)>>1;now=new segment,now->s=0;
	now->ch[0]=pre->ch[0],now->ch[1]=pre->ch[1];
	if(l==r) return now->s=1;
	if(p<=mid) build(now->ch[0],pre->ch[0],p,l,mid);
	else build(now->ch[1],pre->ch[1],p,mid+1,r);
	return push_up(now);
}
int kth(segment* &x,segment* &y,int k,int l,int r)
{
	if(l==r) return l;int mid=(l+r)>>1;
	if(y->ch[0]->s-x->ch[0]->s>=k) return kth(x->ch[0],y->ch[0],k,l,mid);
	k-=y->ch[0]->s-x->ch[0]->s;return kth(x->ch[1],y->ch[1],k,mid+1,r);
}
int query(segment* &x,segment* &y,int s,int t,int l,int r)
{
	int mid=(l+r)>>1,ans=0;if(s<=l&&r<=t) return y->s-x->s;
	if(s<=mid) ans+=query(x->ch[0],y->ch[0],s,t,l,mid);
	if(mid<t) ans+=query(x->ch[1],y->ch[1],s,t,mid+1,r);
	return ans;
}
int main()
{
	scanf("%s",s+1);int q=inn(),n=strlen(s+1);
	for(int i=1;i<=n;i++) a[i]=s[i]-'a'+1;getSA(a,n,30);
	for(int i=1;i<=n;i++) ps[i]=n-sa[i]+1-h[i]+ps[i-1];
//	for(int i=1;i<=n;i++) debug(i)sp,debug(sa[i])sp,debug(h[i])sp,debug(ps[i])ln;
	for(int i=2;i<=n;i++) mnv[i][0]=h[i],Log[i]=Log[i>>1]+1;
	for(int j=1;(1<<j)<=n;j++) for(int i=2;i+(1<<j)-1<=n;i++)
		mnv[i][j]=min(mnv[i][j-1],mnv[i+(1<<(j-1))][j-1]);
	build(T[0],1,n);for(int i=1;i<=n;i++) build(T[i],T[i-1],sa[i],1,n);
	while(q--)
		if(inn()==1)
		{
			lint k1=inln();int k2=inn();
			int L=1,R=n,mid=(L+R)>>1;
			while(L<=R)
			{
				if(ps[mid]<k1) L=mid+1;
				else R=mid-1;mid=(L+R)>>1;
			}
			int len=h[L]+(int)(k1-ps[R]),
				s=L,t=get_t(s,len,n),l=kth(T[s-1],T[t],k2,1,n);
			printf("%d %d\n",l,l+len-1);
		}
		else{
			int l=inn(),r=inn(),x=rk[l],len=r-l+1;
			int s=get_s(x,len),t=get_t(x,len,n);
			printf("%lld %d\n",ps[s-1]+len-h[s],query(T[s-1],T[t],1,l,1,n));
		}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值