1. 程式人生 > 實用技巧 >[CF452E]Three strings

[CF452E]Three strings

目錄

題目

傳送門

題解

演算法一

暴力做,列舉 \(A\) 的字串,在 \(B,C\) 中暴力找,時間複雜度 \(\mathcal O(n^4)\).

演算法二

同樣要在 \(A\) 中列舉字串,但是考慮在 \(L=1\) 時,對於 \(A\) 的每個字元,我們可以在 \(B,C\) 中找出相匹配的,隨著 \(L\) 變大,可能的相同的部分也只可能在之前匹配的字元之後出現,所以我們可以考慮用類似於 vector 一類的東西將位置存下來,時間複雜度 \(\mathcal O(\tt{TLE})\).

演算法三(SA)

考慮使用 \(\tt SA\)

將字串匹配轉換為 \(\tt LCP\) 問題.

首先,處理這種問題的關鍵是我們得將 \(A,B,C\) 首尾相接得到 \(S\),中間使用分隔符隔開,接下來我們稱其為 \(S\)\(A,B,C\) 三個部分.

接下來,字串內部的匹配問題就轉化為字尾的 \(\tt LCP\) 問題,對於原來 \(A,B,C\) 三個串長度為 \(L\) 的匹配,現在即為屬於三個不同部分的字尾的 \(\texttt{LCP} \ge L\) 即可.

但是,對於不同的部分,他們的 \(\tt LCP\) 並非是單調的,比如我們有個字尾排序的陣列長這樣

對於 \(L=4\) 時,可能有這些部分的 \(\tt LCP\)

是滿足條件的

那麼 \(\tt ans[4]\) 就是各個部分的 \(\tt cnt[A] \cdot cnt[B] \cdot cnt[C]\),其中 \(\tt cnt[i]\) 表示屬於 \(i\) 部分的字尾的個數.

但是,隨著 \(L\) 變大,各個部分有可能會斷開,這讓我們非常不好處理,既然 \(L\) 變大會斷開,那麼同樣意味著,如果我們倒著處理 \(L\),那麼隨著 \(L\) 的變小,會有越來越多的部分連線在一起,這個時候我們只需要維護 cnt[i] 即可,而這個部分可以使用並查集來做,並查集合並的時候,我們也可以維護答案.

關鍵程式碼:

const int jzm=1000000007;
/** @brief 全域性答案*/
int sy=0;
/** @brief 對應長度的答案*/
int ans[maxn+5];
struct node{
    // x 是字尾編號
    int x,v;
    inline int operator <(const node rhs)const{
        return v>rhs.v;
    }
}v[maxn+5+5];
int fa[maxn+5];
int find(const int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
int cock(const int x){
    return 1ll*tot[x][1]*tot[x][2]%jzm*tot[x][3]%jzm;
}
inline void merge(int x,int y){
    // printf("merge:> x == %d, y == %d\n",x,y);
    x=find(x),y=find(y);
    if(x==y)return;
    sy-=cock(x);if(sy<0)sy+=jzm;
    sy-=cock(y);if(sy<0)sy+=jzm;
    fa[x]=y;
    rep(j,1,3)tot[y][j]+=tot[x][j];
    sy+=cock(y);
    if(sy>=jzm)sy-=jzm;
}

inline void solve(){
    rep(i,1,n)v[i]=node{sa[i],heit[i]};
    rep(i,1,n)fa[i]=i;
    sort(v+1,v+n+1);
    // rep(i,1,n)printf("v[%d] : x == %d, v == %d\n",i,v[i].x,v[i].v);
    int now=1;
    fep(i,min_len,1){
        while(now<=n && v[now].v>=i){
            // printf("When i == %d, now == %d\n",i,now);
            int pre=sa[rk[v[now].x]-1];
            merge(pre,v[now].x);
            ++now;
        }
        ans[i]=sy;
    }
    rep(i,1,min_len)printf("%d ",ans[i]);
}

演算法四(SAM)

這個方法再等會可能就可以完善了.

一道類似的題

傳送門