BZOJ4566 [Haoi2016]同じキャラクターを探す[SAM]

BZOJ4566 [Haoi2016]同じキャラクターを探す

与えられた2つの文字列が \(sおよびt \) 数のすべてのサブ文字列で一致させることができる2つの文字列を指定するように求め
まずあなたは、私たちが列挙することができます行うことができますどのように考える \(T \) プレフィックス文字列 \(T '\) そして見つける \(T' \) 接尾エネルギーと \(S \) に一致する文字列の数
、この部分の実践をし、需要 \(LCS \) ほとんど
私たち最初の \(S \) 文字列建 \(SAM \) その後、それぞれの状態を計算し \(endposの\) コレクションのサイズ、我々は現在の状態を知っていただきたいと思います \(U \)が ある 最も長い文字列の 現在の状態、接尾サブストリングがどのくらいまで一致させることができます照合できる数は \((len [u] -len [link [u]])\ cdot cnt [u] \)です 。ここで、 \(len [u] \) は状態 の最長の文字列 \(u \)です \(cnt [u] \) の長さ \(endpos \)です セットのサイズ、つまり一致する位置の数に一致可能な長さを掛けたもの。同時に、現在の状態が一致できる場合、サフィックスリンクによって接続されている状態も一致する可能性があります。これは、サフィックスリンクが現在の状態のサフィックスに接続されているためです。文字列のサフィックスは状態 \(u \) と一致する場合があり、次に \(link [u]、link [link [u]] \ cdots \) 一致する必要 があるため、 親ノードを \(parent \)に配置する必要があります ダウンロードに貢献します。現在の状態で最も長い文字列 \(u \) がサフィックスであることを忘れないでください 。照合できる部分文字列の最大数は \(f [u] \)です
\(s \) 文字列を前処理した後 \( SAM \) プレフィックスごとに \(t \) 文字列を実行し ます。これは、前のプレフィックスの後に文字を追加するのと同じです。前の状態が新しい場合は、前の文字列が到達した状態で実行されます。キャラクターの連続する側、最初の点までサフィックスリンクを実行するか、連続する側を持つノードを見つけ、現在の状態の答えを数えます。答えは \(f [link [u]] + cnt [u] \ cdot(matchlen-len [link [u]])\) 、ここで \(matchlen \) \(t \) 文字列 \(t '\) サフィックスの接頭辞、 \(s \) 文字列は最も一致します長い 長さ、現在の状態ではすべての長さが一致しない可能性があるため、直接追加することはできません \(f [u] \) 、特定のコードを見ることができます

//#pragma GCC optimize("O3")
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
typedef long long int LL;
const int MAXN = 4e5+7;
struct SAM{
    int len[MAXN],link[MAXN],ch[MAXN][26],tot,last,cnt[MAXN],c[MAXN],sa[MAXN];
    LL f[MAXN];
    SAM(){ link[0] = -1; }
    void extend(int c){
        int np = ++tot, p = last;
        len[np] = len[p] + 1; cnt[np] = 1;
        while(p!=-1 and !ch[p][c]){
            ch[p][c] = np;
            p = link[p];
        }
        if(p==-1) link[np] = 0;
        else{
            int q = ch[p][c];
            if(len[p] + 1 == len[q]) link[np] = q;
            else{
                int clone = ++tot;
                len[clone] = len[p] + 1;
                link[clone] = link[q];
                memcpy(ch[clone],ch[q],sizeof(ch[q]));
                link[np] = link[q] = clone;
                while(p!=-1 and ch[p][c]==q){
                    ch[p][c] = clone;
                    p = link[p];
                }
            }
        }
        last = np;
    }
    void Radix_sort(){
        for(int i = 0; i <= tot; i++) c[i] = 0;
        for(int i = 0; i <= tot; i++) c[len[i]]++;
        for(int i = 1; i <= tot; i++) c[i] += c[i-1];
        for(int i = tot; i >= 0; i--) sa[c[len[i]]--] = i;
    }
    LL solve(char *s){
        Radix_sort();
        for(int i = tot + 1; i >= 2; i--) cnt[link[sa[i]]] += cnt[sa[i]];
        for(int i = 2; i <= tot + 1; i++){
            int u = sa[i];
            f[u] = f[link[u]] + 1ll * cnt[u] * (len[u] - len[link[u]]); 
        }
        int u = 0, ls = 0;
        LL ret = 0;
        for(int i = 0, l = strlen(s); i < l; i++){
            int c = s[i] - 'a';
            while(u and !ch[u][c]) u = link[u], ls = len[u];
            if(ch[u][c]) u = ch[u][c], ls++;
            if(!u) continue;
            ret = ret + f[link[u]] + (ls-len[link[u]]) * cnt[u];
        }
        return ret;
    }
}sam;
char s[MAXN];
int main(){
    scanf("%s",s);
    for(int i = 0, l = strlen(s); i < l; i++) sam.extend(s[i]-'a');
    scanf("%s",s);
    printf("%lld\n",sam.solve(s));
    return 0;
}

おすすめ

転載: www.cnblogs.com/kikokiko/p/12706855.html