4453: cys就是要拿英魂!
Time Limit: 3 Sec Memory Limit: 128 MBSubmit: 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
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|
3
3
3
3
数据范围:
1<=|S|<=100000
1<=Q<=100000
1<=l<=r<=|S|
HINT
Source
题解:后缀数组+二分
对于区间[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;
}