hdu 6704 後綴數組+RMQ+主席樹

今年CCPC的第一場網絡賽,到現在才補掉。。。

當時還沒學後綴數組,AC自動機,然後賽場上覺得這題一定是用其中一個算法做的,搜AC自動機是幹什麼的,偶然就查到一道類似的可以解決這種題的模板,然後直接學習AC自動機,魔改模板,還過了樣例,最後wa了一下午,現在還不知道爲什麼wa,大概是AC自動機的trie樹根本建不下吧。

題意:給你一個串,q個詢問,問你這l到r的子串在這個串中出現了k次的起始位置

題解:

先跑後綴數組,得到height數組。我們知道,在height[]中,一段值大於等於len的最大區間長度+1就是這個長len的子串在串中出現了幾次,+1是因爲區間最左邊l-1是排序後第一次出現這個子串的位置,所以在之後的st表查詢最小值時要l++(看代碼能發現)

我們用st表建立height數組的區間最小值,這樣子我們就能通過二分求出一段值大於等於len的最大區間長度,即這個子串出現的初始位置,和最終位置。

對sa數組做主席樹,通過上面求出的區間l,r來找尋l到r的第k大的數。

注意多組樣例清空的問題。

#include<bits/stdc++.h>
#define rint register int
#define inv inline void
#define ini inline int
#define maxn 300050
using namespace std;
typedef long long ll;
const ll mod=1000000007;
char s[maxn];
int y[maxn],x[maxn],c[maxn],sa[maxn],rk[maxn],height[maxn];
int n,m,k;
inv get_SA() {
    for(int i=1;i<=m;i++) c[i]=0;
    for (rint i=1; i<=n; ++i) ++c[x[i]=s[i]];
    for (rint i=2; i<=m; ++i) c[i]+=c[i-1];
    for (rint i=n; i>=1; --i) sa[c[x[i]]--]=i;
    for (rint k=1; k<=n; k<<=1) {
        rint num=0;
        for (rint i=n-k+1; i<=n; ++i) y[++num]=i;
        for (rint i=1; i<=n; ++i) if (sa[i]>k) y[++num]=sa[i]-k;
        for (rint i=1; i<=m; ++i) c[i]=0;
        for (rint i=1; i<=n; ++i) ++c[x[i]];
        for (rint i=2; i<=m; ++i) c[i]+=c[i-1];
        for (rint i=n; i>=1; --i) sa[c[x[y[i]]]--]=y[i],y[i]=0;
        swap(x,y);
        x[sa[1]]=1;
        num=1;
        for (rint i=2; i<=n; ++i)
            x[sa[i]]=(y[sa[i]]==y[sa[i-1]] && y[sa[i]+k]==y[sa[i-1]+k]) ? num : ++num;
        if (num==n) break;
        m=num;
    }
}
inv get_height() {
    rint k=0;
    for (rint i=1; i<=n; ++i) rk[sa[i]]=i;
    for (rint i=1; i<=n; ++i) {
        if (rk[i]==1) continue;//第一名height爲0
        if (k) --k;//h[i]>=h[i-1]-1;
        rint j=sa[rk[i]-1];
        while (j+k<=n && i+k<=n && s[i+k]==s[j+k]) ++k;
        height[rk[i]]=k;//h[i]=height[rk[i]];
    }
}
int mmin[maxn][20];
void RMQ_ST(){
    for(int i=1;i<=n;i++){
        mmin[i][0]=height[i];
    }
    int end_j=log(n+0.0)/log(2.0);
    int end_i;
    for(int j=1;j<=end_j;j++){
        end_i=n+1-(1<<j);
        for(int i=1;i<=end_i;i++){
            mmin[i][j]=min(mmin[i][j-1],mmin[i+(1<<(j-1))][j-1]);
        }
    }
}
int QueryMin(int L,int R){
    if(L>R) swap(L,R);
    if(L==R) return n-sa[L]+1;
    L++;
    int k=log(R-L+1.0)/log(2.0);
    return min(mmin[L][k],mmin[R-(1<<k)+1][k]);
}
struct Seg {
    int l, r, sum;
} tr[maxn*40];
int cnt,root[maxn];
void build(int &root,int l,int r){
    root=++cnt;
    tr[root].sum=0;
    if(l==r) return;
    int mid=l+r>>1;
    build(tr[root].l,l,mid);
    build(tr[root].r,mid+1,r);
}
void update(int &x,int y,int l,int r,int pos,int val){
    int pre=x;
    x=++cnt;
    tr[x]=pre?tr[pre]:tr[y];
    if(l==r){
        tr[x].sum=1;
        return;
    }
    int mid=l+r>>1;
    if(pos<=mid) update(tr[x].l,tr[y].l,l,mid,pos,val);
    else update(tr[x].r,tr[y].r,mid+1,r,pos,val);
    tr[x].sum=tr[tr[x].l].sum+tr[tr[x].r].sum;
}
int query(int x,int y,int l,int r,int k){
    if(tr[y].sum-tr[x].sum<k) return -1;
    if(l==r) return l;
    int mid=l+r>>1;
    if(tr[tr[y].l].sum-tr[tr[x].l].sum>=k) return query(tr[x].l,tr[y].l,l,mid,k);
    else return query(tr[x].r,tr[y].r,mid+1,r,k-(tr[tr[y].l].sum-tr[tr[x].l].sum));
}
int getl(int x,int len){
    int l=1,r=x;
    int ans=x;
    while(l<=r){
        int mid=l+r>>1;
        if(QueryMin(mid,x)>=len){
            ans=mid;
            r=mid-1;
        }
        else l=mid+1;
    }
    return ans;
}
int getr(int x,int len){
    int l=x,r=n;
    int ans=x;
    while(l<=r){
        int mid=l+r>>1;
        if(QueryMin(x,mid)>=len){
            ans=mid;
            l=mid+1;
        }
        else r=mid-1;
    }
    return ans;
}
int main() {
    int T;
    scanf("%d",&T);
    while(T--){
        int q;
        memset(root,0,sizeof(root));
        memset(mmin,0,sizeof(mmin));
        memset(height,0,sizeof(height));
        cnt=0;
        scanf("%d%d",&n,&q);
        scanf("%s",s+1);
        int len=strlen(s+1);
        m=200;
        get_SA();
        get_height();
        build(root[0],1,n);
        for(int i=1;i<=n;i++){
            update(root[i],root[i-1],1,n,sa[i],1);
        }
        RMQ_ST();
        while(q--){
            int l,r,tl,tr;
            scanf("%d%d%d",&l,&r,&k);
            tl=getl(rk[l],r-l+1);
            tr=getr(rk[l],r-l+1);
            //printf("!%d %d\n",tl,tr);
            ll ans;
            if(tr-tl+1<k) ans=-1;
            else ans=query(root[tl-1],root[tr],1,n,k);
            printf("%lld\n",ans);
        }
    }


}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章