zoukankan      html  css  js  c++  java
  • HDU-6704 K-th occurrence (后缀自动机father树上倍增建权值线段树合并)


    layout: post
    title: HDU-6704 K-th occurrence (后缀自动机father树上倍增建权值线段树合并)
    author: "luowentaoaa"
    catalog: true
    tags:
    mathjax: true
    - kuangbin
    - 字符串


    传送门

    题意

    给出一个长度为(n)字符串(s)(m)次询问,每次问你字符串(s)区间内的字符串(l,r)(k)次出现的位置。

    思路

    比赛的时候一眼就知道是(sa)但是对于(sa)实在不喜欢,于是比赛的时候整场用(sam)硬怼。已经想到了用主席树记录(endpos)位置,然后从后往前推过去,但是比赛时还不会在(sam)上玩权值线段树合并,赛后学习了一下。

    首先,这题我们可以在(sam)上的每个新开节点上存储这个节点是在原串的那个位置是把。

    然后对于一个串(i)他的(father)肯定是他的(后缀) 所以他的(endpos)肯定也是他(father)(endpos)

    同时一个(father)不仅仅只有(i)这个儿子 所以(father)的继承是从很多儿子那边的来的,于是我们需要搞笑处理继承效果,这里用到了权值线段树合并,线段树下标是(endpos)(s)的位置。

    这样我们就可以通过找到一个点而得出这个点所有的(endpos) ,也可以很快的在权值线段树中找到第(k)大的$endpos (。但是)Q$次查询每次都可能用一个长度(1e5)的串进入(sam)中找这个点的位置,于是我们考虑离线操作,将询问按照(r)排序。但是我们找到了一个点之后可能这个长度太大了,于是我们需要跳到这个点的(father) 知道这个点的长度刚好大于等于我们需要的长度,这样答案才完整。因为(father)的长度是单调的,于是我们可以考虑用树上倍增 快速找到合适的(father)

    于是这题就愉快的结束了。真爽啊。debug都不需要。

    #include<bits/stdc++.h>
    typedef long long ll;
    using namespace std;
    const int maxn=5e5+50;
    int ls[maxn*80],rs[maxn*80],rt[maxn*80],sum[maxn*80],cnt;
    void update(int &o,int l,int r,int pos){
        if(!o)o=++cnt;
        if(l==r){
            sum[o]++;return;
        }
        int mid=(l+r)/2;
        if(pos<=mid)update(ls[o],l,mid,pos);
        else update(rs[o],mid+1,r,pos);
        sum[o]=sum[ls[o]]+sum[rs[o]];
    }
    int merger(int o,int pre,int l,int r){
        if(!o)return pre;if(!pre)return o;
        int k=++cnt;
        if(l==r){
            sum[k]=sum[o]+sum[pre];
            return k;
        }int mid=(l+r)/2;
        ls[k]=merger(ls[o],ls[pre],l,mid);
        rs[k]=merger(rs[o],rs[pre],mid+1,r);
        sum[k]=sum[ls[k]]+sum[rs[k]];
        return k;
    }
    int query(int o,int l,int r,int k){
        if(sum[o]<k)return -1;
        if(l==r)return l;
        int mid=(l+r)/2;
        if(sum[ls[o]]>=k)return query(ls[o],l,mid,k);
        else return query(rs[o],mid+1,r,k-sum[ls[o]]);
    }
    struct Q{
        int l,r,k,id;
        bool operator<(const Q &o)const{
            return r<o.r;
        }
    }my[maxn];
    int n;
    char s[maxn];
    int ans[maxn];
    struct SAM{
        int fa[maxn],ch[maxn][26],maxlen[maxn],tot,last;
        int sz[maxn],a[maxn],b[maxn];
        void init(){
            cnt=0;
            fill(rt,rt+n*80,0);
            fill(ls,ls+n*80,0);
            fill(rs,rs+n*80,0);
            fill(sum,sum+n*80,0);
            tot=last=1;maxlen[1]=fa[1]=0;
            memset(ch[1],0,sizeof(ch[1]));
        }
        void add(int x,int id){
            int np=++tot,p=last;last=np;update(rt[np],1,n,id);
            maxlen[np]=maxlen[p]+1;sz[np]=1;
            memset(ch[np],0,sizeof(ch[np]));
            while(p&&!ch[p][x])ch[p][x]=np,p=fa[p];
            if(!p)fa[np]=1;
            else{
                int q=ch[p][x];
                if(maxlen[q]==maxlen[p]+1)fa[np]=q;
                else{
                    int nq=++tot;
                    memcpy(ch[nq],ch[q],sizeof(ch[q]));
                    fa[nq]=fa[q],fa[np]=fa[q]=nq;
                    maxlen[nq]=maxlen[p]+1;
                    while(p&&ch[p][x]==q)ch[p][x]=nq,p=fa[p];
                }
            }
        }
        int pa[maxn][25];
        void Sort(){///拓扑排序 得到每个集合里的串出现次数
            for(int i=1;i<=tot;i++)a[i]=0;
            for(int i=1;i<=tot;i++)a[maxlen[i]]++;
            for(int i=1;i<=tot;i++)a[i]+=a[i-1];
            for(int i=1;i<=tot;i++)b[a[maxlen[i]]--]=i;
            for(int i=tot;i;i--)sz[fa[b[i]]]+=sz[b[i]];
            for(int i=tot;i;i--){
                int k=b[i];
                pa[k][0]=fa[k];
                rt[fa[k]]=merger(rt[fa[k]],rt[k],1,n);
            }
            for(int i=1;i<=20;i++){
                for(int j=1;j<=tot;j++){
                    int v=pa[j][i-1];
                    pa[j][i]=pa[v][i-1];
                }
            }
        }
        int go(int id,int len){
            for(int i=20;i>=0;i--){
                if(maxlen[pa[id][i]]>=len)id=pa[id][i];
            }
            return id;
        }
        int m;
        void slove(){
            for(int i=1;i<=m;i++){
                cin>>my[i].l>>my[i].r>>my[i].k;
                my[i].id=i;
            }
            sort(my+1,my+1+m);
            int now=1;
            int tail=1;
            for(int i=1;i<=n;i++){
                int x=s[i]-'a';
                now=ch[now][x];
                while(tail<=m&&my[tail].r==i){
                    int len=my[tail].r-my[tail].l+1;
                    int where=go(now,len);
                    int aa=query(rt[where],1,n,my[tail].k);
                    if(aa==-1)ans[my[tail].id]=-1;
                    else{
                        ans[my[tail].id]=aa-len+1;
                    }
                    tail++;
                }
            }
            for(int i=1;i<=m;i++)cout<<ans[i]<<endl;
        }
    }S;
    int main()
    {
        std::ios::sync_with_stdio(false);
        int t;
        cin>>t;
        while(t--){
            cin>>n;cin>>S.m;
            cin>>s+1;
            n=strlen(s+1);S.init();
            for(int i=1;i<=n;i++)S.add(s[i]-'a',i);
            S.Sort();
            S.slove();
        }
        return 0;
    }
    
    
  • 相关阅读:
    [NOI2003][bzoj1507] 文本编辑器 editor [splay]
    GDKOI 游记
    [填坑完毕] 寒假作业计划
    省选算法学习-数据结构-splay
    NOIP2017游记
    真·总结
    赛前
    十一黄(xun)金(lian)周感想
    9.17 模拟赛
    9.14 模拟赛
  • 原文地址:https://www.cnblogs.com/luowentao/p/11415976.html
Copyright © 2011-2022 走看看