HDU-6704 K-th occurrence (后缀自动机father树上倍增建权值线段树合并)

传送门

题意

给出一个长度为\(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;
}

posted @ 2019-08-27 02:00  luowentao  阅读(442)  评论(0编辑  收藏  举报