[bzoj4453]cys就是要拿英魂!

4453: cys就是要拿英魂!

Time Limit: 3 Sec Memory Limit: 128 MB
Submit: 66 Solved: 33
[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|

首先可以看出来,如果对于一个区间 [l,r] 有两个后缀 i,j 他们呢个更优可以分这么几种情况来讨论(假设 i<j ):
①:如果 rank[i]>rank[j] ,那么 i 肯定比j优。
②:如果 rank[i]<rank[j] && lcp(i,j)<rj+1 ,那么j肯定比i优。
③:如果 rank[i]<rank[j] && lcp(i,j)>=rj+1 ,那么i比j更优。
这样如果从后往前扫左端点的话,那么后面的区间就会被分成一块一块的,每一块内的最优值都是一样的,也就是说一个最优值影响的区间是连续的。(也就是当固定了左端点后右端点是单调的。)
每次新扫到一个左端点后,就可以看一下这个左端点影响到了后面哪一段区间。在影响的区间打上标记。
我刚开始本来想二分后面的区间,打标记的时候给线段树区间染色,这样是 O(nlog2n) 的。
可以维护一个栈,栈中的每一个元素对应了一段区间。每次扫到一个左端点后,依次弹出栈顶元素,知道当前这个左端点不会影响到当前栈顶的这个区间了。
还有种情况是可能会影响到最后那个区间的一部分,这样需要在这个区间中二分一下这个区间从哪裂开。
时间复杂度是 O(nlogn) 的。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=100010;
char s[N];
struct Q{int x,y,No;}q[N];
struct S{int v,l,r,No;}stack[N];
int n,m,T,top,t1[N],t2[N],c[N],sa[N],rank[N],height[N],st[N][20],Log[N],ans[N];
inline int in(){
    int x=0;char ch=getchar();
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x;
}
inline bool cmp(int *y,int p,int q,int k){
    int o0,o1;
    o0=p+k>=n?-1:y[p+k];
    o1=q+k>=n?-1:y[q+k];
    return o0==o1&&y[p]==y[q];
}
inline void build_sa(){
    int i,k,p,*x=t1,*y=t2;
    for(i=0;i<m;++i) c[i]=0;
    for(i=0;i<n;++i) ++c[x[i]=s[i]];
    for(i=1;i<m;++i) c[i]+=c[i-1];
    for(i=n-1;~i;--i) sa[--c[x[i]]]=i;
    for(k=1;k<=n;k<<=1){
        for(p=0,i=n-k;i<n;++i) y[p++]=i;
        for(i=0;i<n;++i) if(sa[i]>=k) y[p++]=sa[i]-k;
        for(i=0;i<m;++i) c[i]=0;
        for(i=0;i<n;++i) ++c[x[y[i]]];
        for(i=1;i<m;++i) c[i]+=c[i-1];
        for(i=n-1;~i;--i) sa[--c[x[y[i]]]]=y[i];
        swap(x,y);
        x[sa[0]]=0;m=1;
        for(i=1;i<n;++i) x[sa[i]]=cmp(y,sa[i],sa[i-1],k)?m-1:m++;
        if(m>=n) break;
    }
}
inline void build_height(){
    int i,k=0,j;
    for(i=0;i<n;++i) rank[sa[i]]=i;
    for(i=0;i<n;++i){
        if(!rank[i]) continue;
        k=k?--k:k;
        j=sa[rank[i]-1];
        while(s[j+k]==s[i+k]) ++k;
        height[rank[i]]=k;
    }
    memset(st,127/3,sizeof(st));    
    for(i=0;i<n;++i) st[i][0]=height[i];
    for(j=1;j<=20;++j)
      for(i=0;i+(1<<(j-1))<n;++i)
        st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]);
    for(j=0,i=1;i<=n;++i){
        if((1<<(j+1))<=i) ++j;
        Log[i]=j;
    }
}
inline int LCP(int x,int y){
    if(x>y) swap(x,y);
    int k=Log[y-x];++x;
    return min(st[x][k],st[y-(1<<k)+1][k]);   
}
inline bool CMP(Q x,Q y){return x.x>y.x;}
#define mid (l+r)/2
inline bool check(int x,int y,int z){
    if(rank[x]>rank[y]) return true;
    int len=LCP(rank[x],rank[y]);
    if(len<z-y+1) return false;
    return true;
}
int main(){
    int i,j;
    scanf("%s",s);
    n=strlen(s);
    for(i=0;i<n;++i) m=max(m,(int)s[i]);
    ++m;build_sa();
    build_height();
    T=in();
    for(i=1;i<=T;++i) q[i].x=in()-1,q[i].y=in()-1,q[i].No=i;
    sort(q+1,q+T+1,CMP);
    stack[0].l=n;stack[top=1].v=rank[n-1];
    stack[top].No=stack[top].l=stack[top].r=n-1;
    for(j=1;q[j].x==n-1;++j) ans[q[j].No]=n;
    for(i=n-2;~i&&j<=T;--i){
        int now=top,l,r,flag=0;
        for(;top;--top){
            l=check(i,stack[top].No,stack[top].l);
            r=check(i,stack[top].No,stack[top].r);
            if(l&&r) continue;
            if(!l&&!r) break;
            if(l&&!r){
                flag=1;
                break;
            }
        }
        if(flag){
            now=l=stack[top].l;r=stack[top].r;
            while(l<r){
                if(check(i,stack[top].No,mid)) now=max(now,mid),l=mid+1;
                else r=mid;
            }
            stack[top].l=now+1;
            stack[++top].v=rank[i];
            stack[top].r=now;
            stack[top].l=stack[top].No=i;
        }
        else{
            stack[++top].v=rank[i];
            stack[top].No=stack[top].l=i;
            stack[top].r=stack[top-1].l-1;
        }
        while(q[j].x==i&&j<=T){
            l=1;r=top;now=q[j].y;
            while(l<r){
                if(now>=stack[mid].l&&now<=stack[mid].r) break;
                if(now<stack[mid].l) l=mid+1;
                else r=mid;
            }
            ans[q[j].No]=stack[mid].No+1;
            ++j;
        }
    }
    for(i=1;i<=T;++i) printf("%d\n",ans[i]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值