K-th occurrence HDU-6704(后缀数组+线段树+主席树)

题意:给出一个长度为n的字符串,q次询问,每次询问给出参数 l , r , k l,r,k l,r,k,询问整个字符串中第k个子串 s l s l + 1 . . . . s r s_ls_{l+1}....s_r slsl+1....sr
数据范围: 1 0 5 10^5 105

后缀数组的一些定义:
后缀字符串:以i开始一直到结尾的字符串。

  1. s a [ i ] sa[i] sa[i]:排名为i的后缀字符串。
  2. r a n k [ i ] rank[i] rank[i]:后缀字符串i的排名。
  3. h e i g h t [ i ] height[i] height[i]:排名为 i i i i − 1 i-1 i1的最长公共前缀的长度。

首先预处理出以上数组,建立一个height数组的线段树,维护最大最小值,那么如果要查询字符串 [ l , r ] [l,r] [l,r],那么是不是所有可能的和他前缀相同的一定和他是排名连续的,设 p = r a n k [ l ] p=rank[l] p=rank[l],一定是排名p之前的连续的一段可能为答案,p之后的连续的一段可能为答案。
现在只考虑排名在他之前的,之后的同理。
那么考虑暴力做法,是不是依次查看 h e i g h t [ i ] , 1 &lt; = i &lt; = p height[i],1&lt;=i&lt;=p height[i]1<=i<=p是不是大于等于 r − l + 1 r-l+1 rl+1的,直到找到最近的一个不满足的x,是不是说明 [ x , p ] [x,p] [x,p]之间的都是满足题意要求的子串,那么同样的可以求出其后面的。这是暴力的做法,可以用线段树或者二分加st表优化到log。
然后如果我们知道了所有的满足条件的排名区间 [ r a n k L , r a n R ] [rankL,ranR] [rankL,ranR],是不是说明一定有 r a n k R − r a n L + 1 rankR-ranL+1 rankRranL+1这么多个满足题意要求的子串,那么是不是只要想办法将 [ r a n k L , r a n k R ] [rankL,rankR] [rankL,rankR]内的这些后缀字符串的下标变的有序的话就可以知道第k小的字符串下标是谁了。这个可以将排名从小到大,sa数组作为值域区间,建立主席树,查询区间第k小即可。

#include<bits/stdc++.h>
using namespace std;

const int maxn=1e5+7;

const int ALP=26;
int sa[maxn],Rank[maxn],rank2[maxn],height[maxn],cnt[maxn],*x,*y,mxx[maxn];
char s[maxn];
void radix_sort(int n,int m){
    memset(cnt,0,sizeof(cnt));
    for(int i=0;i<n;i++) cnt[x[y[i]]]++;
    for(int i=1;i<m;i++) cnt[i]+=cnt[i-1];
    for(int i=n-1;i>=0;i--) sa[--cnt[x[y[i]]]]=y[i];
}
void get_sa(char s[],int n){
    int m=128;
    x=Rank,y=rank2;
    for(int i=0;i<n;i++) x[i]=s[i],y[i]=i;
    radix_sort(n,m);
    for(int len=1;len<n;len<<=1){
        int p=0;
        for(int i=n-len;i<n;i++) y[p++]=i;
        for(int i=0; i<n; i++) if(sa[i]>=len) y[p++]=sa[i]-len;
        radix_sort(n,m);
        swap(x,y);
        x[sa[0]]=p=0;
        for(int i=1;i<n;i++){
            if(y[sa[i-1]]==y[sa[i]]&&sa[i-1]+len<n&&sa[i]+len<n&&y[sa[i-1]+len]==y[sa[i]+len])
                x[sa[i]]=p;
            else
                x[sa[i]]=++p;
        }
        m=p+1;
        if(m>=n) break;
    }
    for(int i=0;i<n;i++) Rank[i]=x[i];
}
void get_height(char s[],int n){
    int k=0;
    for(int i=0;i<n;i++){
        if(Rank[i] == 0) continue;
        k=max(0,k-1);
        int j=sa[Rank[i]-1];
        while(i+k<n&&j+k<n&&s[i+k]==s[j+k]) k++;
        height[Rank[i]] = k;
    }
}

int maxx[maxn<<2|1],minn[maxn<<2|1];
int n;
void build(int l,int r,int k){
    if(l==r){
        maxx[k]=minn[k]=height[l];
        return ;
    }
    int mid=(l+r)>>1;
    build(l,mid,k<<1);
    build(mid+1,r,k<<1|1);
    maxx[k]=max(maxx[k<<1],maxx[k<<1|1]);
    minn[k]=min(minn[k<<1],minn[k<<1|1]);
}

int myfindL(int l,int r,int k,int p,int val){
    if(p<1) return -1;
    if(l==r){
        if(maxx[k]<val) return l;
        return -1;
    }
    if(minn[k]>=val) return -1;
    int mid=(l+r)>>1;
    int res=-1;
    if(p<=mid) return myfindL(l,mid,k<<1,p,val);
    if(maxx[k<<1|1]<val) return myfindL(mid+1,r,k<<1|1,p,val);
    res=myfindL(mid+1,r,k<<1|1,p,val);
    if(res!=-1) return res;
    return myfindL(l,mid,k<<1,p,val);
}

int myfindR(int l,int r,int k,int p,int val){
    if(p>n) return -1;
    if(l==r){
        if(maxx[k]<val) return l;
        return -1;
    }
    if(minn[k]>=val) return -1;
    int mid=(l+r)>>1;
    if(p>mid) return myfindR(mid+1,r,k<<1|1,p,val);
    int res=-1;
    if(maxx[k<<1]<val) return myfindR(l,mid,k<<1,p,val);
    res=myfindR(l,mid,k<<1,p,val);
    if(res!=-1) return res;
    return myfindR(mid+1,r,k<<1|1,p,val);

}

struct Tree{
    int lc,rc,sum;
}tree[maxn*30];
int root[maxn];
int tot;

int build(int l,int r){
    int k=++tot;
    tree[k].sum=0;
    if(l==r) return k;
    int mid=(l+r)>>1;
    tree[k].lc=build(l,mid);
    tree[k].rc=build(mid+1,r);
    return k;
}

int updata(int p,int l,int r,int id,int val){
    int k=++tot;
    tree[k]=tree[p];
    ++tree[k].sum;
    if(l==r) return k;
    int mid=(l+r)>>1;
    if(id<=mid) tree[k].lc=updata(tree[p].lc,l,mid,id,val);
    else tree[k].rc=updata(tree[p].rc,mid+1,r,id,val);
    return k;
}

int myfindk(int p,int q,int l,int r,int k){
    if(l==r) return l;
    int mid=(l+r)>>1;
    int val=tree[tree[p].lc].sum-tree[tree[q].lc].sum;
    if(val>=k) return myfindk(tree[p].lc,tree[q].lc,l,mid,k);
    return myfindk(tree[p].rc,tree[q].rc,mid+1,r,k-val);
}

int main(){
    int t,q,l,r,k;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&q);
        scanf("%s",s);
        get_sa(s,n);
        get_height(s,n);
        for(int i=n;i>=1;--i) sa[i]=sa[i-1],++sa[i];
        for(int i=n;i>=1;--i) Rank[i]=Rank[i-1],++Rank[i];
        for(int i=n;i>=1;--i) height[i]=height[i-1];
        if(n==1) Rank[1]=1;
//        for(int i=1;i<=n;++i) cout<<sa[i]<<" ";
//        cout<<endl;
//        for(int i=1;i<=n;++i) cout<<Rank[i]<<" ";
//        cout<<endl;
//        for(int i=1;i<=n;++i) cout<<height[i]<<" ";
//        cout<<endl;
        tot=0;
        build(1,n,1);
        root[0]=build(1,n);
        for(int i=1;i<=n;++i) root[i]=updata(root[i-1],1,n,sa[i],1);
        int ll,rr;
        while(q--){
            scanf("%d%d%d",&l,&r,&k);
            int p=Rank[l];
            ll=myfindL(1,n,1,p,r-l+1);
            rr=myfindR(1,n,1,p+1,r-l+1);
            if(ll==-1) ll=1;
            if(rr==-1) rr=n;
            else --rr;
            //cout<<Rank[l]<<" "<<ll<<" "<<rr<<endl;
            if(rr-ll+1<k) printf("-1\n");
            else printf("%d\n",myfindk(root[rr],root[ll-1],1,n,k));
        }
        //for(int i=0;i<=n;++i) Rank[i]=height[i]=sa[i]=0;
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值