bzoj 4453: cys就是要拿英魂!(后缀数组+二分)

4453: cys就是要拿英魂!

Time Limit: 3 Sec   Memory Limit: 128 MB
Submit: 77   Solved: 39
[ Submit][ Status][ Discuss]

Description

pps又开始dota视频直播了!一群每天被pps虐的蒟蒻决定学习pps的操作技术,他们把pps在这局放的技能记录了下
来,每个技能用一个字符表示。经过研究,蒟蒻们发现字典序更大的连招威力更大。于是所有蒟蒻都想学习pps最
强的连招。但是他们太弱了,不能学会整个视频里的连招,只能学会陈老师一段区间间内的连招,可是这个他们求
不出,于是只好向你求助。为了蒟蒻们不再被pps虐(怎么可能),请你帮帮他们。简化题意:给你一个字符串,
每次询问你一段区间的字典序最大的子串。

Input

第一行是一个字符串S,表示pps放的技能
第二行一个正整数Q,表示询问个数
接下来Q行,每行两个正整数[l,r],表示询问区间[l,r]中的字典序最大的子串。

Output

Q行,每行一个正整数,表示该区间内字典序最大的子串的起始位置。

Sample Input

Lets_go_mod_p!
5
2 2
3 3
2 5
1 10
2 9

Sample Output

2
3
3
3
3
数据范围:
1<=|S|<=100000
1<=Q<=100000
1<=l<=r<=|S|

HINT

Source

[ Submit][ Status][ Discuss]


题解:后缀数组+二分

对于区间[l,r]中的两个后缀i,j(i<j)

若rank[i]>rank[j],i一定比j 优

若rank[i]>rank[j]&&lcp(i,j)<r-j+1,j一定比i优

若rank[i]>rank[j]&&lcp(i,j)>=r-j+1,i一定比j优。

那么我们可以发现一个后缀i不再会对答案产生贡献,是在它右边第一个rank比它大的后缀j,j+lcp(i,j)处。 所以答案是呈区间分布的。

于是我们对于询问的区间左端点从大到小排序。

然后可以维护一个栈,栈中的每一个元素对应了一段区间,表示当前元素在区间[l,r]中贡献最大答案。每次扫到一个左端点后,依次弹出栈顶元素,直到当前这个左端点不会影响到当前栈顶的这个区间了,如果影响区间的一部分,我们就二分一个断点,保证每个位置不重复覆盖。

更新答案的时候二分查找q[i].r所在的区间即可。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define N 100003
using namespace std;
int n,m,p,q,k,ans[N],top;
int rank[3][N],sa[3][N],h[N],a[N],v[N],st[20][N],l[N];
char s[N];
struct data
{
	int l,r,num;
}q1[N],stack[N];
int cmp(data a,data b)
{
	return a.l>b.l;
}
void calc(int sa[N],int rank[N],int SA[N],int Rank[N])
{
	for (int i=1;i<=n;i++) v[rank[sa[i]]]=i;
	for (int i=n;i>=1;i--)
	 if (sa[i]>k)
	  SA[v[rank[sa[i]-k]]--]=sa[i]-k;
	for (int i=n-k+1;i<=n;i++)
	  SA[v[rank[i]]--]=i;
	for (int i=1;i<=n;i++)
	 Rank[SA[i]]=Rank[SA[i-1]]+(rank[SA[i]]!=rank[SA[i-1]]||rank[SA[i]+k]!=rank[SA[i-1]+k]);
}  
void work()
{
	p=0; q=1;
	for (int i=1;i<=n;i++)   v[a[i]]++;
	for (int i=1;i<=300;i++)  v[i]+=v[i-1];
	for (int i=1;i<=n;i++)
	 sa[p][v[a[i]]--]=i;
	for (int i=1;i<=n;i++)
	 rank[p][sa[p][i]]=rank[p][sa[p][i-1]]+(a[sa[p][i]]!=a[sa[p][i-1]]);
	k=1;
	while(k<n)
	 {
	 	calc(sa[p],rank[p],sa[q],rank[q]);
	 	p^=1; q^=1; k<<=1;
	 }
}
void geth()
{
	int k=0;
	for (int i=1;i<=n;i++)
	 if (rank[p][i]==1)  h[rank[p][i]]=0;
	 else
	 {
	 	int j=sa[p][rank[p][i]-1];
	 	while (a[i+k]==a[j+k]) k++;
	 	h[rank[p][i]]=k;
	 	if (k>0) k--;
	 }
}
void solve()
{
	for (int i=1;i<=n;i++) st[0][i]=h[i];
	for (int i=1;i<=17;i++)
	 for (int j=1;j<=n;j++)
	  if (j+(1<<i)-1<=n)
	   st[i][j]=min(st[i-1][j],st[i-1][j+(1<<(i-1))]);
	int k=0;
	for (int i=1;i<=n;i++)
	 {
	 	if (1<<(k+1)<=i) k++;
	 	l[i]=k;
	 }
}
int getlcp(int x,int y)
{
	if (x>y)  swap(x,y);
	int k=l[y-x]; x++;
	return min(st[k][x],st[k][y-(1<<k)+1]);
}
bool pd(int x,int y,int r)
{
	int t=getlcp(rank[p][x],rank[p][y]);
	if (rank[p][x]>rank[p][y]) return true;
	if (rank[p][x]<rank[p][y]&&t>=r-y+1)  return true;
	else return false;
}
int main()
{
	freopen("str.in","r",stdin);
	freopen("str.out","w",stdout);
	scanf("%s",s+1);
	n=strlen(s+1);
	for (int i=1;i<=n;i++)  a[i]=s[i];
	work(); geth(); solve();
	scanf("%d",&m);
	for (int i=1;i<=m;i++)
	 scanf("%d%d",&q1[i].l,&q1[i].r),q1[i].num=i;
	sort(q1+1,q1+m+1,cmp);
	int j=1; top=0;
	for (int i=n;i>=1;i--)
	 {
	 	bool f=false;
	 	while(top)
	 	{
	 		int l=pd(i,stack[top].num,stack[top].l);
	 		int r=pd(i,stack[top].num,stack[top].r);
	 		if (l&&r)  top--;
	 		if (!l&&!r)  break;
	 		if (l&&!r)  {
	 			f=true;
	 			break;
	 		}
	 	}
	 	if (!top) {
	 		stack[++top].num=i;
	 		stack[top].l=i; stack[top].r=n;
	 	}
	 	else
	 	{
	 		if (f)
	 		{
	 			int l=stack[top].l; int r=stack[top].r; int t=0;
	 			while (l<=r)
	 			{
	 				int mid=(l+r)/2;
	 				if(pd(i,stack[top].num,mid)) t=mid,l=mid+1;
	 				else r=mid-1;
	 			}
	 			stack[top].l=t+1;
	 			stack[++top].num=stack[top].l=i;
	 			stack[top].r=t;
	 		}
	 		else
	 		{
	 			stack[++top].num=stack[top].l=i;
	 			stack[top].r=stack[top-1].l-1;
	 		}
	 	}
	 	while (q1[j].l==i&&j<=m)
	 	 {
	 	 	int head=1,tail=top;
	 	 	int t;
	 	 	while (head<=tail)
	 	 	 {
	 	 	 	int mid=(head+tail)/2;
	 	 	 	if (stack[mid].l<=q1[j].r&&stack[mid].r>=q1[j].r)  t=mid;
	 	 	 	if (stack[mid].r>=q1[j].r)  head=mid+1;
				else tail=mid-1;  
	 	 	 }
	 	 	ans[q1[j].num]=stack[t].num;
	 	 	j++;
	 	 }
	 }
	for (int i=1;i<=m;i++)
	 printf("%d\n",ans[i]);
	return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值