[LOJ 2083][UOJ 219][BZOJ 4650][NOI 2016]優秀的拆分

[LOJ 2083][UOJ 219][BZOJ 4650][NOI 2016]優秀的拆分

題意

給定一個字符串 \(S\), 求有多少種將 \(S\) 的子串拆分爲形如 AABB 的拆分方案php

\(|S|\le 30000\) (\(95\%\) 數據 \(|S|\le 2000\))c++

題解

考場上碰見這題直接打95分暴力哈希跑路就完事了吧數組

\(O(n^2)\) 暴力就直接枚舉全部子串看它是否是 AA 型的, 在左右端點處分別標記一下, 而後枚舉斷點把兩邊的方案數乘起來就完事了.優化

考慮優化這個暴力. 咱們枚舉這個 AA 串中 A 的長度 \(l\), 而後每隔 \(l\) 取一個關鍵點, 那麼每一個 AA 串必然會覆蓋兩個關鍵點. 對於每對相鄰的關鍵點 \(a\)\(b\), 咱們計算 \(p=\operatorname{LCP}(S[a:],S[b:])\) 以及 \(s=\operatorname{LCS}(S[:a-1],S[:b-1])\) . 那麼只要 \(p+s\ge l\) 就會有 AA 串出現. 畫畫圖能夠發現 \([a-s,a-(l-p)]\) 均可以是 AA 串的左端點. 直接在差分數組上修改左右端點就能夠了. 注意只能計算同時包含這兩個相鄰的關鍵點的 AA 串, 因此應該和 \([a-l+1,a]\) 取交集. 算完以後前綴和一下按照 \(O(n^2)\) 暴力裏的操做算最終答案就能夠了.ui

參考代碼

#include <bits/stdc++.h>

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

struct SuffixArray{
    char s[MAXN];
    int SA[MAXN];
    int rank[MAXN];
    int stmin[20][MAXN];
    int* height=stmin[0];
    void Build();
    int LCP(int,int);
};
SuffixArray pf,sf;

int n;
int lg[MAXN];
int cnt[MAXN];
char buf[MAXN];
int lcnt[MAXN];
int rcnt[MAXN];
int* x=new int[MAXN];
int* y=new int[MAXN];

int LCP(int,int);
int LCS(int,int);

int main(){
    int T;
    scanf("%d",&T);
    for(int i=2;i<MAXN;i++)
        lg[i]=lg[i>>1]+1;
    while(T--){
        scanf("%s",buf+1);
        n=strlen(buf+1);
        for(int i=1;i<=n;i++)
            pf.s[i]=sf.s[n-i+1]=buf[i];
        pf.Build();
        sf.Build();
        memset(lcnt,0,sizeof(int)*(n+1));
        memset(rcnt,0,sizeof(int)*(n+1));
        for(int len=1;(len<<1)<=n;len++){
            for(int a=1,b;(b=a+len)<=n;a=b){
                int p=LCP(a,b);
                int s=LCS(a-1,b-1);
                if(p+s>=len){
                    int l=std::max(a-len+1,a-s);
                    int r=std::min(a-(len-p),a);
                    ++lcnt[l];
                    --lcnt[r+1];
                    ++rcnt[l+(len<<1)-1];
                    --rcnt[r+(len<<1)];
                }
            }
        }
        for(int i=1;i<=n;i++){
            lcnt[i]+=lcnt[i-1];
            rcnt[i]+=rcnt[i-1];
        }
        intEx ans=0;
        for(int i=1;i<n;i++)
            ans+=1ll*rcnt[i]*lcnt[i+1];
        printf("%lld\n",ans);
    }
    return 0;
}

int LCP(int a,int b){
    return pf.LCP(a,b);
}

int LCS(int a,int b){
    return sf.LCP(n-a+1,n-b+1);
}

int SuffixArray::LCP(int a,int b){
    if(a<1||a>n||b<1||b>n)
        return 0;
    else if(a==b)
        return n-a+1;
    else{
        a=rank[a];
        b=rank[b];
        if(a>b)
            std::swap(a,b);
        int p=lg[b-a];
        ++a;
        return std::min(stmin[p][a],stmin[p][b-(1<<p)+1]);
    }
}

void SuffixArray::Build(){
    int m=127;
    memset(x,0,sizeof(int)*(n+2));
    memset(y,0,sizeof(int)*(n+2));
    memset(cnt,0,sizeof(int)*(m+1));
    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,0,sizeof(int)*(m+1));
        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)
            --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;
    }
    for(int i=1;(1<<i)<=n;i++){
        for(int j=2;j<=n;j++){
            stmin[i][j]=stmin[i-1][j];
            if(j+(1<<(i-1))<=n)
                stmin[i][j]=std::min(stmin[i][j],stmin[i-1][j+(1<<(i-1))]);
        }
    }
}

相關文章
相關標籤/搜索