[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]);
}
發佈了155 篇原創文章 · 獲贊 191 · 訪問量 14萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章