HDU6704 K-th occurrence(后缀数组+二分+主席树)

2021-03-27 03:25

阅读:590

标签:图片   mes   lap   build   c++   维护   etl   二分   kmp   

根据题意,寻找子串出现的第k次的开头。寻找第k大,一般可以想到用主席树来维护。

但是这题还需要更多的转化,首先想到我们如果想知道子串匹配,一个可以考虑kmp,但是因为询问过多,不太科学。

一般还有两种,一种是哈希算法,一种是后缀数组求lcp。考虑哈希算法,感觉可做性不是很大,因为他要多次匹配。考虑后缀数组

我们发现一个重要的信息,也就是后缀数组的height数组是随着位置的远离而变小的,因此每次的询问其实都有一段固定的区间。这个区间可以通过两次二分求取。

这样我们的任务就变成了在后缀数组的排名序列上,二分出一段满足条件的区间,我们在这上面求取第k大值,后者可以用主席树维护。

技术图片技术图片
#includeusing namespace std;
using namespace std;
typedef long long ll;
const int N=1e5+10;
int n,q;
char t[N];
int sa[N],rk[N],od[N],cnt[N],id[N],px[N];
ll h[N];
int root[N];
ll f[N][26];
ll lg[N],idx;
struct node{
    int l,r;
    int cnt;
}tr[40*N];
bool cmp(int x, int y, int w){
  return od[x]==od[y]&&od[x+w]==od[y+w];
}
void build(int &rt,int l,int r){
    rt=++idx;
    tr[rt]={l,r,0};
    if(l==r)
        return;
    int mid=l+r>>1;
    build(tr[rt].l,l,mid);
    build(tr[rt].r,mid+1,r);
}
void da(int n,int m){
    int i;
    memset(cnt,0,sizeof cnt);
    for(i=1;i;
    for(i=1;i1];
    for(i=n;i>=1;i--) sa[cnt[rk[i]]--]=i;
    int p;
    for(int w=1;w1,m=p){
        p=0;
        for(i=n;i>n-w;i--)
            id[++p]=i;
        for(i=1;i)
            if(sa[i]>w)
                id[++p]=sa[i]-w;
        memset(cnt,0,sizeof cnt);
        for(i=1;i;
        for(i=1;i1];
        for(i=n;i>=1;i--) sa[cnt[px[i]]--]=id[i];
        for(i=0;irk[i];
        for(p=0,i=1;i){
            rk[sa[i]]=cmp(sa[i-1],sa[i],w)?p:++p;
        }
    }
}
void st(int n){
    int i,j;
    for(i=1;i)
        f[i][0]=h[i];
    lg[1]=0;
    for(i=2;i>1]+1;
    for(j=1;j){
        for(i=1;i+(11){
            f[i][j]=min(f[i][j-1],f[i+(11)][j-1]);
        }
    }
}
int query(int l,int r){
    if(l>r)
        swap(l,r);
    l++;
    int len=lg[r-l+1];
    return min(f[l][len],f[r-(11][len]);
}
void get_height(int n){
    int i;
    for(i=1;i){
        if(rk[i]==1)
            continue;
        int a=max(h[rk[i-1]]-1,1ll*0);
        while(i+a1]+a])
            a++;
        h[rk[i]]=a;
    }
    st(n);
}
int getL(int l,int r,int len,int x){
    while(lr){
        int mid=l+r>>1;
        if(query(mid,x)>=len){
            r=mid;
        }
        else{
            l=mid+1;
        }
    }
    return l;
}
int getR(int l,int r,int len,int x){
    while(lr){
        int mid=l+r+1>>1;
        if(query(mid,x)>=len)
            l=mid;
        else
            r=mid-1;
    }
    return l;
}
int getans(int p,int q,int l,int r,int k){
    if(l==r)
        return l;
    int cnt=tr[tr[q].l].cnt-tr[tr[p].l].cnt;
    int mid=l+r>>1;
    if(kcnt)
    return getans(tr[p].l,tr[q].l,l,mid,k);
    else
    return getans(tr[p].r,tr[q].r,mid+1,r,k-cnt);
}
int solve(int l,int r,int k){
    int len=r-l+1;
    int L=getL(1,rk[l],len,rk[l]);
    int R=getR(rk[l],n,len,rk[l]);
    if(k>R-L+1)
        return -1;
    return getans(root[L-1],root[R],1,n,k);
}
void add(int &rt,int l,int r,int p,int pos,int k){
    rt=++idx;tr[rt]=tr[p];
    if(l==r){
        tr[rt].cnt+=k;
        return ;
    }
    int mid=l+r>>1;
    if(posmid) add(tr[rt].l,l,mid,tr[p].l,pos,k);
    else add(tr[rt].r,mid+1,r,tr[p].r,pos,k);
    tr[rt].cnt=tr[tr[rt].l].cnt+tr[tr[rt].r].cnt;
}
int main(){
    //ios::sync_with_stdio(false);
    int T;
    cin>>T;
    while(T--){
        scanf("%d%d",&n,&q);
        scanf("%s",t+1);
        int lent=strlen(t+1);
        da(lent,150);
        get_height(lent);
        idx=0;
        build(root[0],1,n);
        for(int i=1;i){
            int pos=sa[i];
            add(root[i],1,n,root[i-1],pos,1);
        }
        while(q--){
            int l,r,k;
            scanf("%d%d%d",&l,&r,&k);
            coutendl;
        }
    }
}
View Code

 

HDU6704 K-th occurrence(后缀数组+二分+主席树)

标签:图片   mes   lap   build   c++   维护   etl   二分   kmp   

原文地址:https://www.cnblogs.com/ctyakwf/p/13674576.html


评论


亲,登录后才可以留言!