[BZOJ 3230]類似子串

[BZOJ 3230]類似子串

題意

給定一個長度爲 \(n\) 的字符串以及 \(q\) 組查詢, 每組查詢給定 \(a\)\(b\), 求在全部本質不一樣子串中排名第 \(a\) 和第 \(b\) 的串的最長公共前綴與最長公共後綴的平方和.php

\(n,q\le 1\times 10^5\).c++

題解

後綴數組板子題.麻麻我終於會用後綴數組辣數組

原本想接着用SAM的...可是發現多組查詢排名爲 \(k\) 的本質不一樣子串以我對SAM的理解好像不能作...因而就用了SA.ui

最長公共先後綴顯然對正反串建兩個SA就能夠 \(O(n\log n)\rightarrow O(1)\) 算了. 關鍵在於怎麼找到這兩個子串.spa

把全部後綴排序後, 顯然每個後綴對本質不一樣子串的貢獻就是和前一個後綴相同的部分以外的部分了. 那麼求出全部 \(height\) 後咱們也能知道前 \(k\) 個後綴總共產生了多少本質不一樣的子串. 直接二分找到子串左端點而後根據差值算出子串長度就能夠了.code

由於要有兩個SA, 因此簡單地封到結構體裏會比較好寫. 然而這根本不算封裝blog

參考代碼

#include <bits/stdc++.h>

const int MAXN=1e5+10;
typedef long long intEx;

struct SuffixArray{
    int* x;
    int* y;
    char s[MAXN];
    int SA[MAXN];
    int lg[MAXN];
    int cnt[MAXN];
    int rank[MAXN];
    intEx sum[MAXN];
    int height[MAXN];
    int st[18][MAXN];
    SuffixArray():x(new int[MAXN]),y(new int[MAXN]){}
    void Build();
    int LCP(int,int);
    std::pair<int,int> Query(intEx);
};
SuffixArray a,b;

int n;
int q;

inline intEx Sqr(intEx);

int main(){
    scanf("%d%d",&n,&q);
    scanf("%s",a.s+1);
    for(int i=1;i<=n;i++)
        b.s[i]=a.s[i];
    std::reverse(b.s+1,b.s+n+1);
    a.Build();
    b.Build();
    for(int i=0;i<q;i++){
        intEx x,y;
        scanf("%lld%lld",&x,&y);
        auto p1=a.Query(x);
        auto p2=a.Query(y);
        if(p1.first==-1||p2.first==-1)
            puts("-1");
        else{
            intEx ans=Sqr(std::min(a.LCP(p1.first,p2.first),std::min(p1.second,p2.second)));
            p1.first=n-(p1.first+p1.second-1)+1;
            p2.first=n-(p2.first+p2.second-1)+1;
            ans+=Sqr(std::min(b.LCP(p1.first,p2.first),std::min(p1.second,p2.second)));
            printf("%lld\n",ans);
        }
    }
    return 0;
}

int SuffixArray::LCP(int x,int y){
    if(x==y)
        return INT_MAX;
    else{
        int l=rank[x],r=rank[y];
        if(l>r)
            std::swap(l,r);
        ++l;
        int len=r-l+1;
        return std::min(st[lg[len]][l],st[lg[len]][r-(1<<lg[len])+1]);
    }
}

std::pair<int,int> SuffixArray::Query(intEx k){
    int p=std::lower_bound(sum+1,sum+n+1,k)-sum;
    if(p==n+1)
        return {-1,-1};
    else
        return {SA[p],n-(sum[p]-k)-SA[p]+1};
}

void SuffixArray::Build(){
    int m=127;
    for(int i=1;i<=n;i++)
        ++cnt[x[i]=s[i]];
    for(int i=1;i<=m;i++)
        cnt[i]+=cnt[i-1];
    for(int i=n;i>=1;i--)
        SA[cnt[x[i]]--]=i;
    for(int k=1;k<n;k<<=1){
        int p=0;
        for(int i=n-k+1;i<=n;i++)
            y[++p]=i;
        for(int i=1;i<=n;i++)
            if(SA[i]>k)
                y[++p]=SA[i]-k;
        memset(cnt+1,0,sizeof(int)*m);
        for(int i=1;i<=n;i++)
            ++cnt[x[i]];
        for(int i=1;i<=m;i++)
            cnt[i]+=cnt[i-1];
        for(int i=n;i>=1;i--)
            SA[cnt[x[y[i]]]--]=y[i];
        std::swap(x,y);
        x[SA[1]]=1;
        p=1;
        for(int i=2;i<=n;i++)
            x[SA[i]]=(y[SA[i]]==y[SA[i-1]]&&y[SA[i]+k]==y[SA[i-1]+k])?p:++p;
        if(p>=n)
            break;
        m=p;
    }
    for(int i=1;i<=n;i++)
        rank[SA[i]]=i;
    int k=0;
    for(int i=1;i<=n;i++){
        if(rank[i]==1)
            continue;
        if(k!=0)
            --k;
        int j=SA[rank[i]-1];
        while(i+k<=n&&j+k<=n&&s[i+k]==s[j+k])
            ++k;
        height[rank[i]]=k;
        st[0][rank[i]]=k;
    }
    for(int i=1;i<=n;i++)
        sum[i]=sum[i-1]+(n-SA[i]+1)-height[i];
    for(int i=1;(1<<i)<=n;i++){
        ++lg[1<<i];
        for(int j=2;j<=n;j++){
            st[i][j]=st[i-1][j];
            if(j+(1<<(i-1))<=n)
                st[i][j]=std::min(st[i][j],st[i-1][j+(1<<(i-1))]);
        }
    }
    for(int i=1;i<=n;i++)
        lg[i]+=lg[i-1];
}

inline intEx Sqr(intEx x){
    return x*x;
}

相關文章
相關標籤/搜索