HDU 6704 K-th occurrence 后缀数组+二分+主席树

http://www.caiyiwen.tech/article/6.html

  • C - K-th occurrence
    • 很好的后缀数组题,训练时因为还不会后缀数组,故未做,学习后缀数组后独立写出。
    • 首先第一个问题是如何找出给定子串所有出现的位置。显然,l就是给定子串的首位置,而这个子串其他出现的位置一定是sa[rank[l-1]], sa[rank[l-2]], …或sa[rank[l+1]], sa[rank[l+2]], …
    • 即我们需要找到一个[L, R]使得sa[i] (i∈[L, R])均为给定子串出现的首位置,这就需要lcp(sa[i], sa[rank[l]])≥r-l+1对任意i∈[L, R]成立。已知求出任意两个后缀的lcp均可是O(1)时间(使用RMQ),那么我们可以用二分将此题转化为判定性问题。
    • 首先判断是否满足单调性,对任意i<L,lcp(sa[i], sa[rank[l]])显然是<r-l+1,对任意i>R也是如此,所以,可以分两段二分,找[1, rank[l]]区间内的最小值即为L,[rank[l], n]区间内的最大值即为R。
    • 得到区间[L, R]之后,我们已经找到了所有给定串出现的位置,但是需要找第k次出现,也就是区间[L, R]内第k小的数。“区间第k小”是一个经典的主席树问题,这里不再赘述。

代码:

#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=100010;
#define F(x) ((x)/3+((x)%3==1?0:tb))
#define G(x) ((x)<tb?(x)*3+1:((x)-tb)*3+2)
int wa[maxn*3],wb[maxn*3],wv[maxn*3],wss[maxn*3];
int r[maxn*3],sa[maxn*3];
inline int c0(int*r,int a,int b)
{
	return r[a]==r[b]&&r[a+1]==r[b+1]&&r[a+2]==r[b+2];
}
inline int c12(int k,int*r,int a,int b)
{
	if(k==2)
	{
		return r[a]<r[b]||(r[a]==r[b]&&c12(1,r,a+1,b+1));
	}
	return r[a]<r[b]||(r[a]==r[b]&&wv[a+1]<wv[b+1]);
}
inline void sort(int*r,int*a,int*b,int n,int m)
{
	int i;
	for(i=0;i<n;++i)
	{
		wv[i]=r[a[i]];
	}
	for(i=0;i<m;++i)
	{
		wss[i]=0;
	}
	for(i=0;i<n;++i)
	{
		++wss[wv[i]];
	}
	for(i=1;i<m;++i)
	{
		wss[i]+=wss[i-1];
	}
	for(i=n-1;i>=0;--i)
	{
		b[--wss[wv[i]]]=a[i];
	}
}
void dc3(int*r,int*sa,int n,int m)
{
	int i,j,*rn=r+n,*san=sa+n,ta=0,tb=(n+1)/3,tbc=0,p;
	r[n]=r[n+1]=0;
	for(i=0;i<n;++i)
	{
		if(i%3)
		{
			wa[tbc++]=i;
		}
	}
	sort(r+2,wa,wb,tbc,m);
	sort(r+1,wb,wa,tbc,m);
	sort(r,wa,wb,tbc,m);
	for(p=1,rn[F(wb[0])]=0,i=1;i<tbc;++i)
	{
		rn[F(wb[i])]=c0(r,wb[i-1],wb[i])?p-1:p++;
	}
	if(p<tbc)
	{
		dc3(rn,san,tbc,p);
	}
	else
	{
		for(i=0;i<tbc;++i)
		{
			san[rn[i]]=i;
		}
	}
	for(i=0;i<tbc;++i)
	{
		if(san[i]<tb)
		{
			wb[ta++]=san[i]*3;
		}
	}
	if(n%3==1)
	{
		wb[ta++]=n-1;
	}
	sort(r,wb,wa,ta,m);
	for(i=0;i<tbc;++i)
	{
		wv[wb[i]=G(san[i])]=i;
	}
	for(i=0,j=0,p=0;i<ta&&j<tbc;++p)
	{
		sa[p]=c12(wb[j]%3,r,wa[i],wb[j])?wa[i++]:wb[j++];
	}
	for(;i<ta;++p)
	{
		sa[p]=wa[i++];
	}
	for(;j<tbc;++p)
	{
		sa[p]=wb[j++];
	}
}
int Rank[maxn],height[maxn];
inline void da(int*str,int*sa,int*Rank,int*height,int n,int m)
{
	for(int i=n;i<n*3;++i)
	{
		str[i]=0;
	}
	dc3(str,sa,n+1,m);
	int i,j,k=0;
	for(i=0;i<=n;++i)
	{
		Rank[sa[i]]=i;
	}
	for(i=0;i<n;++i)
	{
		if(k)
		{
			--k;
		}
		j=sa[Rank[i]-1];
		while(str[i+k]==str[j+k])
		{
			++k;
		}
		height[Rank[i]]=k;
	}
}
int mm[maxn],best[20][maxn];
inline void initRMQ(int n)
{
	int i,j,a,b;
	for(mm[0]=-1,i=1;i<=n;i++)
	{
		mm[i]=((i&(i-1))==0)?mm[i-1]+1:mm[i-1];
	}
	for(i=1;i<=n;i++)
	{
		best[0][i]=i;
	}
	for(i=1;i<=mm[n];i++)
	{
		for(j=1;j<=n+1-(1<<i);j++)
		{
			a=best[i-1][j];
			b=best[i-1][j+(1<<(i-1))];
			if(height[a]<height[b])
			{
				best[i][j]=a;
			}
			else
			{
				best[i][j]=b;
			}
		}
	}
}
inline int askRMQ(int a,int b)
{
	int t=mm[b-a+1];
	b-=(1<<t)-1;
	a=best[t][a];
	b=best[t][b];
	return height[a]<height[b]?a:b;
}
inline int lcp(int a,int b)
{
	a=Rank[a];
	b=Rank[b];
	if(a==b)
	{
		return height[a];
	}
	if(a>b)
	{
		swap(a,b);
	}
	return height[askRMQ(a+1,b)];
}
struct Node
{
	int num,lch,rch;
	Node():num(0),lch(-1),rch(-1){}
}tree[maxn*20];
int tot,rt[maxn];
#define init() tot=0
inline void pushup(int p)
{
	tree[p].num=tree[tree[p].lch].num+tree[tree[p].rch].num;
}
int build(int l,int r)
{
	int p=tot++;
	tree[p]=Node();
	if(l==r)
	{
		tree[p].num=0;
		return p;
	}
	int mid=(l+r)>>1;
	tree[p].lch=build(l,mid);
	tree[p].rch=build(mid+1,r);
	pushup(p);
	return p;
}
int add(int p,int l,int r,int x,int y,int z)
{
	int cp=tot++;
	tree[cp]=Node();
	if(x<=l&&r<=y)
	{
		tree[cp].num=tree[p].num+r-l+1;
		return cp;
	}
	int mid=(l+r)>>1;
	if(x<=mid)
	{
		tree[cp].lch=add(tree[p].lch,l,mid,x,y,z);
	}
	else
	{
		tree[cp].lch=tree[p].lch;
	}
	if(mid<y)
	{
		tree[cp].rch=add(tree[p].rch,mid+1,r,x,y,z);
	}
	else
	{
		tree[cp].rch=tree[p].rch;
	}
	pushup(cp);
	return cp;
}
int query(int x,int y,int l,int r,int k)
{
	if(l==r)
	{
		return l;
	}
	int left_num=tree[tree[y].lch].num-tree[tree[x].lch].num;
	int mid=(l+r)>>1;
	if(left_num>=k)
	{
		return query(tree[x].lch,tree[y].lch,l,mid,k);
	}
	return query(tree[x].rch,tree[y].rch,mid+1,r,k-left_num);
}
char s[maxn];
inline int get_prev(int pos,int len)
{
	int l=1,r=pos,mid;
	while(l<r)
	{
		mid=(l+r)>>1;
		if(lcp(sa[mid],sa[pos])>=len)
		{
			r=mid;
		}
		else
		{
			l=mid+1;
		}
	}
	return l;
}
inline int get_suf(int pos,int len,int n)
{
	int l=pos,r=n,mid;
	while(l<r)
	{
		mid=(l+r+1)>>1;
		if(lcp(sa[mid],sa[pos])>=len)
		{
			l=mid;
		}
		else
		{
			r=mid-1;
		}
	}
	return l;
}
int main()
{
	int T;
	scanf("%d",&T);
	while(T--)
	{
		int n,q;
		scanf("%d%d%s",&n,&q,s);
		for(int i=0;i<n;++i)
		{
			r[i]=s[i]-'a'+1;
		}
		da(r,sa,Rank,height,n,27);
		initRMQ(n);
		init();
		rt[0]=build(0,n-1);
		for(int i=1;i<=n;++i)
		{
			rt[i]=add(rt[i-1],0,n-1,sa[i],sa[i],sa[i]);
		}
		int l,r,k;
		while(q--)
		{
			scanf("%d%d%d",&l,&r,&k);
			int first=get_prev(Rank[l-1],r-l+1);
			int last=get_suf(Rank[l-1],r-l+1,n);
			if(last-first+1<k)
			{
				printf("-1\n");
			}
			else
			{
				int ans=query(rt[first-1],rt[last],0,n-1,k);
				printf("%d\n",ans+1);
			}
		}
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值