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


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

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

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

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

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

    #include<bits/stdc++.h>
    using 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<=n;i++) cnt[rk[i]=t[i]]++;
        for(i=1;i<=m;i++) cnt[i]+=cnt[i-1];
        for(i=n;i>=1;i--) sa[cnt[rk[i]]--]=i;
        int p;
        for(int w=1;w<n;w<<=1,m=p){
            p=0;
            for(i=n;i>n-w;i--)
                id[++p]=i;
            for(i=1;i<=n;i++)
                if(sa[i]>w)
                    id[++p]=sa[i]-w;
            memset(cnt,0,sizeof cnt);
            for(i=1;i<=n;i++) cnt[px[i]=rk[id[i]]]++;
            for(i=1;i<=m;i++) cnt[i]+=cnt[i-1];
            for(i=n;i>=1;i--) sa[cnt[px[i]]--]=id[i];
            for(i=0;i<=n;i++) od[i]=rk[i];
            for(p=0,i=1;i<=n;i++){
                rk[sa[i]]=cmp(sa[i-1],sa[i],w)?p:++p;
            }
        }
    }
    void st(int n){
        int i,j;
        for(i=1;i<=n;i++)
            f[i][0]=h[i];
        lg[1]=0;
        for(i=2;i<=n;i++) lg[i]=lg[i>>1]+1;
        for(j=1;j<=lg[n];j++){
            for(i=1;i+(1<<j)-1<=n;i++){
                f[i][j]=min(f[i][j-1],f[i+(1<<j-1)][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-(1<<len)+1][len]);
    }
    void get_height(int n){
        int i;
        for(i=1;i<=n;i++){
            if(rk[i]==1)
                continue;
            int a=max(h[rk[i-1]]-1,1ll*0);
            while(i+a<=n&&t[i+a]==t[sa[rk[i]-1]+a])
                a++;
            h[rk[i]]=a;
        }
        st(n);
    }
    int getL(int l,int r,int len,int x){
        while(l<r){
            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(l<r){
            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(k<=cnt)
        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(pos<=mid) 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<=n;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);
                cout<<solve(l,r,k)<<endl;
            }
        }
    }
    View Code
    没有人不辛苦,只有人不喊疼
  • 相关阅读:
    双指针法
    secureCRT安装与激活
    [Python之路] Python各类常用库整理
    [工具] Atom Markdown编辑器
    [Python之路] object类中的特殊方法
    [Python自学] Flask框架 (5) (DBUtils数据库链接池、wtforms)
    [Python自学] Flask框架 (4) (Request&Session上下文管理、redis保存session、App&g上下文管理)
    [刷题] Leetcode算法 (2020-3-1)
    [工具] Window10搭建Django开发环境
    [算法] 动态规划 (1) (工作最优收入)
  • 原文地址:https://www.cnblogs.com/ctyakwf/p/13674576.html
Copyright © 2020-2023  润新知