字串資結

にゃー -by toshi

AC by Astrayt

SA by Thanksone

謝一說他要講SA

AC自動機

AC上來講

他能做啥

一次匹配一大堆字串
但他不會幫你刷AC

還記得Trie嗎?

還記得KMP嗎?

發動魔法卡

在Trie上建構Failure

小畫家時間

實作

struct ACA{
    vector<vector<int>> trie;
    vector<int> f;
    void new_vertex(){trie.pb(vector<int> (26, 0)); f.pb(0);}
    void init(){trie.clear(); f.clear(); new_vertex();}
    void add_string(string s, int w){
        int cur = 0;
        for(char c:s){
            if(trie[cur][c]) cur = trie[cur][c];
            else {
                new_vertex();
                cur = trie[cur][c] = trie.size() - 1;
            }
        }
    }
    void fail(){
        queue<int> bfs;
        for(int i = 0; i < 26; ++i) if(trie[0][i]) bfs.pp(trie[0][i]);
        while(bfs.size()){
            int u = bfs.front(); bfs.pop();
            for(int i = 0; i < 26; ++i){
                skip(!trie[u][i])
                int v = trie[u][i], bacc = f[u];
                while(bacc && !trie[bacc][i]) bacc = f[bacc];
                if(trie[bacc][i]) bacc = trie[bacc][i];
                f[v] = bacc; bfs.pp(v);
            }
        }
    }
}AC;

用在DP上

#include <bits/stdc++.h>
using namespace std;
#define starburst ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
typedef long long ll;
#define int ll
#define vi vector<int>
#define pii pair<int,int>
#define pb push_back
#define pp push
#define ff first
#define ss second

void change(string &s){
    for(char &c:s){
        if(c == 'r') c = 0;
        else if(c == 'g') c = 1;
        else if(c == 'b') c = 2;
        else c = 3;
    }
}

struct AutoChicken{
    vector<vector<int>> trie;
    vector<int> val, f;
    void new_vertex(){trie.pb(vector<int> (3, 0)); val.pb(0); f.pb(0);}
    void init(){trie.clear(); val.clear(); f.clear(); new_vertex();}
    void add_string(string s, int w){
        int cur = 0;
        for(char c:s){
            if(trie[cur][c]) cur = trie[cur][c];
            else {
                new_vertex();
                cur = trie[cur][c] = trie.size() - 1;
            }
        }
        val[cur] += w;
    }
    void fail(){
        queue<int> bfs;
        for(int i = 0; i <= 2; ++i) if(trie[0][i]) bfs.pp(trie[0][i]);
        while(bfs.size()){
            int u = bfs.front(); bfs.pop();
            for(int i = 0; i <= 2; ++i){
                skip(!trie[u][i])
                int v = trie[u][i], bacc = f[u];
                while(bacc && !trie[bacc][i]) bacc = f[bacc];
                if(trie[bacc][i]) bacc = trie[bacc][i];
                f[v] = bacc;
                val[v] += val[bacc];
                bfs.pp(v);
            }
        }
    }
}AC;

void solve(){
    AC.init();
    int n, k; cin >> n >> k;
    for(int i = 1; i <= k; ++i){
        string t; int w;
        cin >> t >> w;
        change(t); AC.add_string(t, w);
    }
    string s; cin >> s; change(s);
    AC.fail();
    vector<int> dp[2]; int S = AC.trie.size();
    dp[0].assign(S, -1e18); dp[1].assign(S, -1e18); dp[0][0] = 0;
    for(int i = 0; i < n; ++i){
        dp[1].assign(S, -1e18);
        for(int j = 0; j < S; ++j){
            skip(dp[0][j] < 0)
            for(int k = 0; k <= 2; ++k){
                skip(s[i] != 3 && k != s[i])
                int cur = j;
                while(cur && !AC.trie[cur][k]) cur = AC.f[cur];
                if(AC.trie[cur][k]) cur = AC.trie[cur][k];
                dp[1][cur] = max(dp[1][cur], dp[0][j] + AC.val[cur]);
            }
        }
        dp[0] = dp[1];
    }
    cout << *max_element(dp[0].begin(), dp[0].end());
}

signed main(){
    starburst
    int t = 1; //cin >> t;
    while(t--) solve();
}

Reference

Suffix Array

簡稱 SA

把後綴排序

 \(banana \implies \begin{bmatrix} a \\ ana \\ anana \\ banana \\ na \\ nana \end{bmatrix}\)

直接排序可能要花 \(O(N^2logN)\),很慢

所以我們砸倍增法

\(banana \implies \begin{bmatrix} 1 \ anana (1, 0) \\ 1 \ ana (1, 0) \\ 1 \ a (1, 0) \\ 2 \ banana (2, 0) \\ 3 \ nana (3, 0) \\ 3 \ na (3, 0) \end{bmatrix} \implies \begin{bmatrix} 1 \ a (1, 0) \\ 2 \ anana (1, 3) \\ 2 \ ana (1, 3) \\ 3 \ banana (2, 1) \\ 4 \ nana (3, 1) \\ 4 \ na (3, 1) \end{bmatrix} \\ \implies \begin{bmatrix} 1 \ a (1, 0) \\ 2 \ ana (2, 1) \\ 3 \ anana (2, 2) \\ 4 \ banana (3, 4) \\ 5 \ na (4, 0) \\ 6 \ nana (4, 4) \end{bmatrix} \implies \begin{bmatrix} 1 \ a (1, 0) \\ 2 \ ana (2, 0) \\ 3 \ anana (3, 1) \\ 4 \ banana (4, 5) \\ 5 \ na (5, 0) \\ 6 \ nana (6, 0) \end{bmatrix}\)

複雜度

倍增次數 \(O(logN) \times\) 排序時間 \(O(NlogN) \\ = O(Nlog^2N)\)

優化 : 可以用 radix sort,排序時間 \(O(N)\)

#include <bits/stdc++.h>
#define pb push_back
using namespace std;
array<int, 100004> SA, RNK, L, R;
array<vector<int>, 100004> V;
void sort(array<int, 100004> &A, int n){
    for(int i = 0; i < n; i++){
        V[A[SA[i]]].pb(SA[i]);
    }
    for(int i = 0, k = 0; i < max(26, n); i++){
        for(int v : V[i]) SA[k++] = v;
        V[i].clear();
    }
}
void SUF(string &S){
    int n = S.size(), cnt = -1, l = -1, r = -1;
    for(int i = 0; i < n; i++){
        SA[i] = n - i - 1, RNK[i] = L[i] = S[i] - 'a';
    }
    sort(L, n);
    for(int k = 1; k < n; k <<= 1, cnt = l = r = -1){
        for(int i = 0; i < n; i++){
            L[i] = RNK[i], R[i] = i + k < n? RNK[i + k] : 0;
        }
        sort(R, n), sort(L, n);
        for(int i = 0; i < n; i++){
            if(L[SA[i]] != l || R[SA[i]] != r) cnt++;
            RNK[SA[i]] = cnt, l = L[SA[i]], r = R[SA[i]];
        }
    }
}

SA 能作的東西

在 SA 上面二分搜

在 SA 上面砸資結 like 線段樹

在 SA 上面作最長共同前綴 (LCP)

LCP 建法

void CP(string &S){
    int n = S.size();
    for(int i = 0; i < n; i++) RNK[SA[i]] = i;
    for(int i = 0, k, cp = 0; i < n; i++){
        if(!RNK[i]) continue;
        k = SA[RNK[i] - 1];
        if(cp) cp--;
        while(S[i + cp] == S[k + cp]) cp++;
        LCP[RNK[i]] = cp;
    }
}

題目

AC 作法

#include <bits/stdc++.h>
using namespace std;
#define starburst ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
typedef long long ll;
//#define int ll
#define vi vector<int>
#define pb push_back
#define pp push
#define pof pop_front
#define pob pop_back
#define skip(x) if(x)continue;

void turn(string &s){
    for(char &c:s) c -= 'a';
}

struct AhoCorasick{
    vector<vector<int>> trie;
    vector<int> f, cnt, vec;
    void new_vertex(){
        trie.pb(vector<int> (26, 0));
        f.pb(0); cnt.pb(0);
    }
    void init(){
        trie.clear(), f.clear(), cnt.clear();
        new_vertex(); vec = {0};
    }
    void add_string(string s, int &id){
        int cur = 0;
        for(char c:s){
            if(trie[cur][c]) cur = trie[cur][c];
            else {
                new_vertex();
                cur = trie[cur][c] = trie.size() - 1;
            }
        }
        id = cur;
    }
    void fail(){
        queue<int> bfs;
        for(int i = 0; i < 26; ++i) if(trie[0][i]) bfs.pp(trie[0][i]);
        while(bfs.size()){
            int u = bfs.front(); bfs.pop(); vec.pb(u);
            for(int i = 0; i < 26; ++i){
                skip(!trie[u][i])
                int v = trie[u][i], bacc = f[u];
                while(bacc && !trie[bacc][i]) bacc = f[bacc];
                if(trie[bacc][i]) bacc = trie[bacc][i];
                f[v] = bacc; bfs.pp(v);
            }
        }
    }
    void DP(){
        for(int i = vec.size() - 1; i >= 0; --i){
            cnt[f[vec[i]]] += cnt[vec[i]];
        }
    }
};

void solve(){
    AhoCorasick AC;
    AC.init();
    string s; cin >> s;
    vector<int> id;
    int n = s.size(), k, len = 0; cin >> k;
    turn(s);
    while(k--){
        string t; cin >> t;
        id.pb(0);
        turn(t); AC.add_string(t, id[id.size() - 1]);
        len += t.size();
        if(len > 100000){
            len = 0;
            AC.fail();
            for(int i = 0, cur = 0; i < n; ++i){
                while(cur && !AC.trie[cur][s[i]]) cur = AC.f[cur];
                if(AC.trie[cur][s[i]]) cur = AC.trie[cur][s[i]];
                ++AC.cnt[cur];
            }
            AC.DP();
            for(int i:id) cout << AC.cnt[i] << '\n';
            id.clear(), AC.init();
        }
    }
    AC.fail();
    for(int i = 0, cur = 0; i < n; ++i){
        while(cur && !AC.trie[cur][s[i]]) cur = AC.f[cur];
        if(AC.trie[cur][s[i]]) cur = AC.trie[cur][s[i]];
        ++AC.cnt[cur];
    }
    AC.DP();
    for(int i:id) cout << AC.cnt[i] << '\n';
}

signed main(){
    starburst
    int t = 1; cin >> t;
    while(t--) solve();
}

SA 作法

#include <bits/stdc++.h>
#define pb push_back
using namespace std;
array<int, 10004> SA, RNK, L, R;
array<vector<int>, 10004> V;
void sort(array<int, 10004> &A, int n){
    for(int i = 0; i < n; i++){
        V[A[SA[i]]].pb(SA[i]);
    }
    for(int i = 0, k = 0; i < max(26, n); i++){
        for(int v : V[i]) SA[k++] = v;
        V[i].clear();
    }
}
void SUF(string &S){
    int n = S.size(), cnt = -1, l = -1, r = -1;
    for(int i = 0; i < n; i++){
        SA[i] = n - i - 1, RNK[i] = L[i] = S[i] - 'a';
    }
    sort(L, n);
    for(int k = 1; k < n; k <<= 1, cnt = l = r = -1){
        for(int i = 0; i < n; i++){
            L[i] = RNK[i], R[i] = i + k < n? RNK[i + k] : 0;
        }
        sort(R, n), sort(L, n);
        for(int i = 0; i < n; i++){
            if(L[SA[i]] != l || R[SA[i]] != r) cnt++;
            RNK[SA[i]] = cnt, l = L[SA[i]], r = R[SA[i]];
        }
    }
}
int cmp(string &S, string &T, int p, int t){
    for(int i = 0; i < min((int)T.size(), (int)S.size() - p); i++){
        if(T[i] != S[p + i]) return T[i] > S[p + i];
    }
    return T.size() + p > S.size()? 1 : t;
}
int BIS(string &S, string &T, int t){
    int p = -1;
    for(int i = 1 << 13; i; i >>= 1){
        if(p + i < S.size() && cmp(S, T, SA[p + i], t)) p += i;
    }
    return p;
}
signed main(){
    int t, q;
    string T, P;
    cin >> t;
    while(t--){
        cin >> T >> q;
        SUF(T);
        while(q--){
            cin >> P;
            cout << BIS(T, P, 1) - BIS(T, P, 0) << "\n";
        }
    }
    return 0;
}

SA 作法

#include <bits/stdc++.h>
#define pb push_back
using namespace std;
array<int, 100004> SA, RNK, L, R;
array<vector<int>, 100004> V;
void sort(array<int, 100004> &A, int n){
    for(int i = 0; i < n; i++){
        V[A[SA[i]]].pb(SA[i]);
    }
    for(int i = 0, k = 0; i < max(26, n); i++){
        for(int v : V[i]) SA[k++] = v;
        V[i].clear();
    }
}
void SUF(string &S){
    int n = S.size();
    for(int i = 0; i < n; i++){
        SA[i] = n - i - 1, RNK[i] = L[i] = S[i] - 'a';
    }
    sort(L, n);
    for(int k = 1; k < n; k <<= 1){
        for(int i = 0; i < n; i++){
            L[i] = RNK[i], R[i] = i + k < n? RNK[i + k] : 0;
        }
        sort(R, n), sort(L, n);
        for(int i = 0, cnt = -1, l = -1, r = -1; i < n; i++){
            if(L[SA[i]] != l || R[SA[i]] != r) cnt++;
            RNK[SA[i]] = cnt, l = L[SA[i]], r = R[SA[i]];
        }
    }
}
int cmp(string &S, string &T, int p, int t){
    for(int i = 0; i < min(T.size(), S.size() - p); i++){
        if(T[i] != S[p + i]) return T[i] > S[p + i];
    }
    return T.size() + p > S.size()? 1 : t;
}
int BIS(string &S, string &T, int t){
    int p = -1;
    for(int i = 1 << 16; i; i >>= 1){
        if(p + i < S.size() && cmp(S, T, SA[p + i], t)) p += i;
    }
    return p;
}
signed main(){
    string S, T;
    int k;
    cin >> S >> k;
    SUF(S);
    while(k--){
        cin >> T;
        cout << (BIS(S, T, 1) - BIS(S, T, 0)? "YES\n" : "NO\n");
    }
    return 0;
}

SA 作法

#include <bits/stdc++.h>
#define pb push_back
using namespace std;
array<int, 100004> SA, RNK, L, R;
array<vector<int>, 100004> V;
void sort(array<int, 100004> &A, int n){
    for(int i = 0; i < n; i++){
        V[A[SA[i]]].pb(SA[i]);
    }
    for(int i = 0, k = 0; i < max(26, n); i++){
        for(int v : V[i]) SA[k++] = v;
        V[i].clear();
    }
}
void SUF(string &S){
    int n = S.size();
    for(int i = 0; i < n; i++){
        SA[i] = n - i - 1, RNK[i] = L[i] = S[i] - 'a';
    }
    sort(L, n);
    for(int k = 1; k < n; k <<= 1){
        for(int i = 0; i < n; i++){
            L[i] = RNK[i], R[i] = i + k < n? RNK[i + k] : 0;
        }
        sort(R, n), sort(L, n);
        for(int i = 0, cnt = -1, l = -1, r = -1; i < n; i++){
            if(L[SA[i]] != l || R[SA[i]] != r) cnt++;
            RNK[SA[i]] = cnt, l = L[SA[i]], r = R[SA[i]];
        }
    }
}
int cmp(string &S, string &T, int p, int t){
    for(int i = 0; i < min(T.size(), S.size() - p); i++){
        if(T[i] != S[p + i]) return T[i] > S[p + i];
    }
    return T.size() + p > S.size()? 1 : t;
}
int BIS(string &S, string &T, int t){
    int p = -1;
    for(int i = 1 << 16; i; i >>= 1){
        if(p + i < S.size() && cmp(S, T, SA[p + i], t)) p += i;
    }
    return p;
}
signed main(){
    string S, T;
    int k;
    cin >> S >> k;
    SUF(S);
    while(k--){
        cin >> T;
        cout << BIS(S, T, 1) - BIS(S, T, 0) << "\n";
    }
    return 0;
}

SA 作法

#include <bits/stdc++.h>
#define pb push_back
#define mid ((l + r) >> 1)
#define lc (p << 1)
#define rc ((p << 1) | 1)
using namespace std;
array<int, 100004> SA, RNK, L, R;
array<int, 400004> seg;
array<vector<int>, 100004> V;
void pull(int p){
    seg[p] = min(seg[lc], seg[rc]);
}
void update(int p, int l, int r, int x, int v){
    if(x < l || x > r) return;
    if(l == r){
        seg[p] = v;
        return;
    }
    update(lc, l, mid, x, v);
    update(rc, mid + 1, r, x, v);
    pull(p);
}
int query(int p, int l, int r, int ql, int qr){
    if(qr < l || ql > r) return 1 << 30;
    if(ql <= l && qr >= r) return seg[p];
    return min(query(lc, l, mid, ql, qr), query(rc, mid + 1, r, ql, qr));
}
void sort(array<int, 100004> &A, int n){
    for(int i = 0; i < n; i++){
        V[A[SA[i]]].pb(SA[i]);
    }
    for(int i = 0, k = 0; i < max(26, n); i++){
        for(int v : V[i]) SA[k++] = v;
        V[i].clear();
    }
}
void SUF(string &S){
    int n = S.size();
    for(int i = 0; i < n; i++){
        SA[i] = n - i - 1, RNK[i] = L[i] = S[i] - 'a';
    }
    sort(L, n);
    for(int k = 1; k < n; k <<= 1){
        for(int i = 0; i < n; i++){
            L[i] = RNK[i], R[i] = i + k < n? RNK[i + k] : 0;
        }
        sort(R, n), sort(L, n);
        for(int i = 0, cnt = -1, l = -1, r = -1; i < n; i++){
            if(L[SA[i]] != l || R[SA[i]] != r) cnt++;
            RNK[SA[i]] = cnt, l = L[SA[i]], r = R[SA[i]];
        }
    }
    for(int i = 0; i < n; i++) update(1, 0, n, i, SA[i] + 1);
}
int cmp(string &S, string &T, int p, int t){
    for(int i = 0; i < min(T.size(), S.size() - p); i++){
        if(T[i] != S[p + i]) return T[i] > S[p + i];
    }
    return T.size() + p > S.size()? 1 : t;
}
int BIS(string &S, string &T, int t){
    int p = -1;
    for(int i = 1 << 16; i; i >>= 1){
        if(p + i < S.size() && cmp(S, T, SA[p + i], t)) p += i;
    }
    return p;
}
signed main(){
    string S, T;
    int k, l, r;
    cin >> S >> k;
    SUF(S);
    while(k--){
        cin >> T;
        l = BIS(S, T, 0), r = BIS(S, T, 1);
        cout << (r - l? query(1, 0, S.size(), l + 1, r) : -1) << "\n";
    }
    return 0;
}

SA 作法

#include <bits/stdc++.h>
#define pb push_back
#define int long long
using namespace std;
array<int, 100004> SA, RNK, L, R, LCP;
array<vector<int>, 100004> V;
void sort(array<int, 100004> &A, int n){
    for(int i = 0; i < n; i++){
        V[A[SA[i]]].pb(SA[i]);
    }
    for(int i = 0, k = 0; i < max(26ll, n); i++){
        for(int v : V[i]) SA[k++] = v;
        V[i].clear();
    }
}
void SUF(string &S){
    int n = S.size(), cnt = -1, l = -1, r = -1;
    for(int i = 0; i < n; i++){
        SA[i] = n - i - 1, RNK[i] = L[i] = S[i] - 'a';
    }
    sort(L, n);
    for(int k = 1; k < n; k <<= 1, cnt = l = r = -1){
        for(int i = 0; i < n; i++){
            L[i] = RNK[i], R[i] = i + k < n? RNK[i + k] : 0;
        }
        sort(R, n), sort(L, n);
        for(int i = 0; i < n; i++){
            if(L[SA[i]] != l || R[SA[i]] != r) cnt++;
            RNK[SA[i]] = cnt, l = L[SA[i]], r = R[SA[i]];
        }
    }
}
int CP(string &S){
    int n = S.size(), sum = 0;
    for(int i = 0; i < n; i++) RNK[SA[i]] = i;
    for(int i = 0, k, cp = 0; i < n; i++){
        if(!RNK[i]) continue;
        k = SA[RNK[i] - 1];
        if(cp) cp--;
        while(S[i + cp] == S[k + cp]) cp++;
        LCP[RNK[i]] = cp;
        sum += cp;
    }
    return sum;
}
signed main(){
    int n;
    string S;
    cin >> S;
    n = S.size(), SUF(S);
    cout << n * (n + 1) / 2 - CP(S) << "\n";
    return 0;
}

SA 作法

#include <bits/stdc++.h>
#define pb push_back
using namespace std;
array<int, 100004> SA, RNK, L, R;
array<vector<int>, 100004> V;
void sort(array<int, 100004> &A, int n){
    for(int i = 0; i < n; i++){
        V[A[SA[i]]].pb(SA[i]);
    }
    for(int i = 0, k = 0; i < max(26, n); i++){
        for(int v : V[i]) SA[k++] = v;
        V[i].clear();
    }
}
void SUF(string &S){
    int n = S.size();
    for(int i = 0; i < n; i++){
        SA[i] = n - i - 1, RNK[i] = L[i] = S[i] - 'a';
    }
    sort(L, n);
    for(int k = 1; k < n; k <<= 1){
        for(int i = 0; i < n; i++){
            L[i] = RNK[i], R[i] = i + k < n? RNK[i + k] : 0;
        }
        sort(R, n), sort(L, n);
        for(int i = 0, cnt = -1, l = -1, r = -1; i < n; i++){
            if(L[SA[i]] != l || R[SA[i]] != r) cnt++;
            RNK[SA[i]] = cnt, l = L[SA[i]], r = R[SA[i]];
        }
    }
}
string CP(string &S){
    int n = S.size(), lng = 0, p;
    string ans;
    for(int i = 0; i < n; i++) RNK[SA[i]] = i;
    for(int i = 0, k, cp = 0; i < n; i++){
        if(!RNK[i]) continue;
        k = SA[RNK[i] - 1];
        if(cp) cp--;
        while(S[i + cp] == S[k + cp]) cp++;
        if(lng < cp) lng = cp, p = i;
    }
    for(int i = 0; i < lng; i++) ans += S[p + i];
    if(!lng) ans = "-1";
    return ans;
}
signed main(){
    string S, T;
    cin >> S;
    SUF(S);
    cout << CP(S) << "\n";
    return 0;
}

SA 作法

#include <bits/stdc++.h>
#define pb push_back
#define int long long
using namespace std;
array<int, 100004> SA, RNK, L, R, LCP;
array<vector<int>, 100004> V;
void sort(array<int, 100004> &A, int n){
    for(int i = 0; i < n; i++){
        V[A[SA[i]]].pb(SA[i]);
    }
    for(int i = 0, k = 0; i < max(26ll, n); i++){
        for(int v : V[i]) SA[k++] = v;
        V[i].clear();
    }
}
void SUF(string &S){
    int n = S.size(), cnt = -1, l = -1, r = -1;
    for(int i = 0; i < n; i++){
        SA[i] = n - i - 1, RNK[i] = L[i] = S[i] - 'a';
    }
    sort(L, n);
    for(int k = 1; k < n; k <<= 1, cnt = l = r = -1){
        for(int i = 0; i < n; i++){
            L[i] = RNK[i], R[i] = i + k < n? RNK[i + k] : 0;
        }
        sort(R, n), sort(L, n);
        for(int i = 0; i < n; i++){
            if(L[SA[i]] != l || R[SA[i]] != r) cnt++;
            RNK[SA[i]] = cnt, l = L[SA[i]], r = R[SA[i]];
        }
    }
}
void CP(string &S){
    int n = S.size();
    for(int i = 0; i < n; i++) RNK[SA[i]] = i;
    for(int i = 0, k, cp = 0; i < n; i++){
        if(!RNK[i]) continue;
        k = SA[RNK[i] - 1];
        if(cp) cp--;
        while(S[i + cp] == S[k + cp]) cp++;
        LCP[RNK[i]] = cp;
    }
}
string CNT(string &S, int k){
    int n = S.size(), p;
    string ans;
    for(p = 0; p < n; p++){
        if(n - SA[p] - LCP[p] >= k) break;
        k -= n - SA[p] - LCP[p];
    }
    for(int i = SA[p]; i < SA[p] + LCP[p] + k; i++) ans += S[i];
    return ans;
}
signed main(){
    int k;
    string S, T;
    cin >> S >> k;
    SUF(S), CP(S);
    cout << CNT(S, k) << "\n";
    return 0;
}

SA 作法

#include <bits/stdc++.h>
#define int long long
#define pb push_back
#define mid ((l + r) >> 1)
#define lc (p << 1)
#define rc ((p << 1) + 1)
using namespace std;
array<int, 100004> SA, RNK, F, L;
array<int, 400004> seg, tag;
array<vector<int>, 100004> buk;
void pull(int p){
    seg[p] = seg[lc] + seg[rc];
}
void push(int p, int l, int r){
    seg[lc] += (mid - l + 1) * tag[p];
    seg[rc] += (r - mid) * tag[p];
    tag[lc] += tag[p];
    tag[rc] += tag[p];
    tag[p] = 0;
}
void update(int p, int ql, int qr, int v, int l, int r){
    if(ql > r || qr < l) return;
    if(ql <= l && qr >= r){
        seg[p] += (r - l + 1) * v;
        tag[p] += v;
        return;
    }
    push(p, l, r);
    update(lc, ql, qr, v, l, mid);
    update(rc, ql, qr, v, mid + 1, r);
    pull(p);
}
int query(int p, int ql, int qr, int l, int r){
    if(ql > r || qr < l) return 0;
    if(ql <= l && qr >= r) return seg[p];
    push(p, l, r);
    return query(lc, ql, qr, l, mid) + query(rc, ql, qr, mid + 1, r);
}
void sort(array<int, 100004> &A, int n){
    int cnt = 0;
    for(int i = 0; i < n; i++){
        buk[A[SA[i]]].pb(SA[i]);
    }
    for(int i = 0; i < max(n, 26ll); i++){
        for(int x : buk[i]) SA[cnt++] = x;
        buk[i].clear();
    }
}
void suf(string &S){
    int n = S.size(), cnt, ff, ll;
    for(int i = 0; i < n; i++){
       SA[i] = n - i - 1;
       RNK[i] = F[i] = S[i] - 'a';
    }
    sort(F, n);
    for(int k = 1; k < n; k <<= 1){
        cnt = ff = ll = -1;
        for(int i = 0; i < n; i++){
            F[i] = RNK[i];
            L[i] = i + k < n? RNK[i + k] : 0;
        }
        sort(L, n);
        sort(F, n);
        for(int i = 0; i < n; i++){
            if(F[SA[i]] == ff && L[SA[i]] == ll) RNK[SA[i]] = cnt;
            else RNK[SA[i]] = ++cnt;
            ff = F[SA[i]], ll = L[SA[i]];
        }
    }
    for(int i = 0; i < n; i++){
        update(1, i, i, n - SA[i], 0, n);
    }
}
int bis(string &S, int t, int l, int r, char c){
    int p = l - 1;
    for(int i = 1 << 16; i > 0; i >>= 1){
        if(p + i <= r && S[SA[p + i] + t] <= c) p += i;
    }
    return p;
}
char cha(string &S, int k, int t, int l, int r){
    int p, n = S.size();
    char cl = 'a', cr = 'z', cm;
    while(cl != cr){
        cm = (cl + cr) >> 1;
        p = bis(S, t, l, r, cm);
        if(query(1, l, p, 0, n) < k) cl = cm + 1;
        else cr = cm;
    }
    return cl;
}
string see(string &S, int k){
    int n = S.size(), l = 0, r = n - 1, t = 0, tmp;
    char p;
    string s;
    while(k > 0){
        p = cha(S, k, t, l, r);
        tmp = l;
        l = bis(S, t, l, r, p - 1) + 1;
        k -= query(1, tmp, l - 1, 0, n);
        r = bis(S, t, l, r, p);
        update(1, l, r, -1, 0, n);
        k -= r - l + 1;
        s += p;
        t++;
    }
    return s;
}
signed main(){
    int k;
    string S;
    cin >> S >> k;
    suf(S);
    cout << see(S, k) << "\n";
    return 0;
}

SA 作法

#include <bits/stdc++.h>
#define int long long
#define pb push_back
using namespace std;
array<int, 100004> SA, RNK, F, L, sum;
array<vector<int>, 100004> buk;
void sort(array<int, 100004> &A, int n){
    int cnt = 0;
    for(int i = 0; i < n; i++){
        buk[A[SA[i]]].pb(SA[i]);
    }
    for(int i = 0; i < max(n, 26ll); i++){
        for(int x : buk[i]){
            SA[cnt++] = x;
        }
        buk[i].clear();
    }
}
void suf(string &S){
    int n = S.size(), cnt, ff, ll;
    for(int i = 0; i < n; i++){
        SA[i] = n - i - 1;
        RNK[i] = F[i] = S[i] - 'a';
    }
    sort(F, n);
    for(int j = 1; j < n; j <<= 1){
        cnt = ff = ll = -1;
        for(int i = 0; i < n; i++){
            F[i] = RNK[i];
            L[i] = i + j < n? RNK[i + j] : 0;
        }
        sort(L, n);
        sort(F, n);
        for(int i = 0; i < n; i++){
            if(F[SA[i]] == ff && L[SA[i]] == ll) RNK[SA[i]] = cnt;
            else RNK[SA[i]] = ++cnt;
            ff = F[SA[i]], ll = L[SA[i]];
        }
    }
}
void lcp(string &S){
    int n = S.size(), cp = 0, k;
    for(int i = 0; i < n; i++) RNK[SA[i]] = i;
    for(int i = 0; i < n; i++){
        if(!RNK[i]){
            sum[0]++;
            sum[n - i]--;
            continue;
        }
        k = SA[RNK[i] - 1];
        if(cp) cp--;
        while(S[i + cp] == S[k + cp]) cp++;
        sum[cp]++;
        sum[n - i]--;
    }
}
signed main(){
    int ans = 0;
    string S;
    cin >> S;
    suf(S);
    lcp(S);
    for(int i = 0; i < S.size(); i++){
        ans += sum[i];
        cout << ans << " ";
    }
    return 0;
}
Made with Slides.com