Hướng dẫn cho Mã hóa dãy ngoặc


Chỉ sử dụng khi thực sự cần thiết như một cách tôn trọng tác giả và người viết hướng dẫn này.

Chép code từ bài hướng dẫn để nộp bài là hành vi có thể dẫn đến khóa tài khoản.

Authors: SPyofgame


\(\color{red}{\text{Spoiler Alert}_{{}_{{}^{{}^{v2.0}}}}}\)

\(\color{red}{\text{Khuyến khích bạn đọc trước khi đọc phần lời giải xin hãy thử code ra thuật của mình dù nó có sai hay đúng}}\)

\(\color{red}{\text{Sau đó từ phần bài giải và thuật toán trước đó mà đối chiếu, rút nhận xét với thuật của mình và thu được bài học (không lãng phí thời gian đâu).}}\)



\(\color{orange}{\text{Hint 1 <Brute-forces>}}\)

  • Thử tạo từng xâu dãy ngoặc

Với mỗi xâu mình kiểm tra xem nó có tạo ra được dãy \(p\) hay không

Từ đó cập nhật kết quả

Đầu tiên ta tìm các cặp đối xứng và sắp xếp theo thứ tự xuất hiện của \(")"\)

Sau đó ta sẽ đếm số lượng \(")"\) trên từng đoạn đối xứng và vứt vảo dãy \(w\)

Cuối cùng thì xuất dãy \(w\) hoặc in ra \(-1\) nếu không tồn tại dãy như thế


\(\color{goldenrod}{\text{Approach <Brute-force>}}\)

  • Hàm \(main()\) sẽ thử nhận, xử lí và xuất kết quả

Đầu tiên sẽ nhận các phần tử mảng \(p\)

Sau đó sẽ thực hiện \(build()\) các dãy

Rồi mình sẽ xuất \(-1\) nếu không tồn tại dãy thỏa mãn hoặc xuất dãy \(w\) ra nếu tìm được

\(_{_{\color{purple}{\text{Complexity : }} O(2^{2 \times n} \times n ^ 2)\ \color{purple}{\text{auxiliary time}}\ ||\ O(n)\ \color{purple}{\text{auxiliary memory}}}}\)

C++
int main()
{
    /// Nhan mang p
    n = readInt();
    p.resize(n);
    for (int &x : p) cin >> x;

    /// Xu li
    build();

    /// Xuat ket qua
    if (invalid) /// neu khong tim thay
    {
        cout << -1;
        return 0;
    }
    for (int x : w) cout << x << ' '; /// neu tim thay
    return 0;
}

  • Hàm \(build()\) để xây xâu \(s\) gồm \("("\)\(")"\)

Xâu gồm \(n\) kí tự \("("\)\(")"\) nên khi \(|s| = 2 * n\) ta sẽ kiểm tra và cập nhật

Ngược lại, từ một xâu \(s\) mình sẽ thử điền từng kí tự \("("\) hoặc \(")"\) vào bên phải cùng của \(s\)

\(_{_{\color{purple}{\text{Complexity : }} O(2 ^ {2 \times n} \times n ^ 2)\ \color{purple}{\text{auxiliary time}}\ ||\ O(n)\ \color{purple}{\text{auxiliary memory}}}}\)

C++
void build(string s = "") {
    if (s.size() == 2 * n) /// Da build xong xau s
    {
        if (check(s)) update(s); /// Neu kiem tra xau s thoa man thi cap nhat day w
        return ;
    }

    build(s + '('); /// thu xay them dau ngoac mo
    build(s + ')'); /// thu xay them dau ngoac dong
}

  • Hàm \(check()\) để kiểm tra nếu từ xâu \(s\) ta mã hóa được ra xâu \(p\)

Duyệt qua từng phần tử, nếu phần tử hiện tại là \(")"\) thì đếm số lượng phía trước

Đưa số lượng đếm được vào mảng mã hóa \(p\) hiện tại, kiểm tra xem \(p\) hiện tại có bằng \(p\) yêu cầu không

\(_{_{\color{purple}{\text{Complexity : }} O(n ^ 2)\ \color{purple}{\text{auxiliary time}}\ ||\ O(n)\ \color{purple}{\text{auxiliary memory}}}}\)

C++
bool check(string s) { /// kiem tra xau s co ma hoa ra day p hay khong
    vector<int> current_p; /// mang ma hoa p hien tai
    for (int cur = 0; cur < s.size(); ++cur)
    {
        if (s[cur] == ')') /// neu ki tu hien tai la ")"
        {
            int cnt = 0;
            for (int pre = 0; pre < cur; ++pre)
            {
                if (s[pre] == '(') /// dem so luong phan tu "("
                {
                    cnt++;
                }
            }
            current_p.push_back(cnt); /// them phan tu do vao mang p hien tai
        }
    }
    return current_p == p; /// neu mang p hien tai la mang p ma hoa duoc thi return true
}

  • Hàm \(update()\) để mã hóa từ xâu \(s\) ra xâu \(w\)

Đánh dấu lại là tồn tại dãy \(w\) thỏa mãn

Mình sẽ tìm các cặp tương ứng \("("\)\(")"\) bằng ở phần \(getPairs()\)

Sau đó mình sắp xếp các dãy ở phần \(bubbleSort()\)

Cuối cùng mình sẽ mã hóa sang xâu \(w\) ở phần \(encode()\)

\(_{_{\color{purple}{\text{Complexity : }} O(n ^ 2)\ \color{purple}{\text{auxiliary time}}\ ||\ O(n)\ \color{purple}{\text{auxiliary memory}}}}\)

C++
void update(string s) { /// cap nhat lai day w
    invalid = false; /// danh dau da tim thay

    vector<pair<int, int> > suit; /// mang cac cap tuong ung
    getPairs(suit, s); /// tim cac cap tuong ung
    bubbleSort(suit);  /// sap xep cac cap tuong ung
    encode(suit, s);   /// ma hoa cac cap tuong ung
}

  • Hàm \(getPairs()\) để tìm các cặp tương ứng \("("\)\(")"\)

Gọi mảng \(pos\) là vị trí của từng kí tự trong xâu

Mỗi lần mình duyệt qua xâu, nếu tìm thấy một cặp \("()"\)

Mình xóa 2 kí tự khỏi xâu \(s\)

Thêm một cặp tương ứng là vị trí của 2 kí tự đó vào mảng \(suit\)

Mình cập nhật vị trí của cả mảng \(pos\) từ vị trí bị xóa dồn cả phần sau lên 2 ô

Giờ gán lại \(pos.size = s.size\) cho nó hợp lí

Sau đó mình cho con trỏ chạy lại từ đầu

\(_{_{\color{purple}{\text{Complexity : }} O(n ^ 2)\ \color{purple}{\text{auxiliary time}}\ ||\ O(n)\ \color{purple}{\text{auxiliary memory}}}}\)

C++
void getPairs(vector<pair<int, int> > &suit, string s) { /// lay tung cap "(" va ")" tuong ung
    int n = s.size();
    vector<int> pos(n); /// Vi tri cua no trong xau ban dau
    for (int i = 0; i < n; ++i)
        pos[i] = i + 1; /// Khoi tao vi tri dau tien

    for (int i = 0; i + 1 < n; ++i)
    {
        if (s[i] == '(' && s[i + 1] == ')') /// Neu tim thay mot cap tuong ung
        {
            suit.push_back(make_pair(pos[i], pos[i + 1])); /// them cap do vao mang
            s.erase(i, 2); /// xoa phan tu di

            for (int j = i; j + 2 < n; ++j) /// don mang len 2 don vi
                pos[j] = pos[j + 2];

            pos.resize(s.size());

            i = -1; /// duyet lai tu dau
            // cout << s << endl; /// debug
        }
    }
}

  • Hàm \(bubbleSort()\) mình sẽ sắp xếp cả mảng \(getPairs()\) theo \(")"\) tăng dần

Duyệt qua từng cặp phần tử, nếu một cặp lớn hơn cặp kia thì đổi vị trí hai cặp

\(_{_{\color{purple}{\text{Complexity : }} O(n ^ 2)\ \color{purple}{\text{auxiliary time}}\ ||\ O(1)\ \color{purple}{\text{auxiliary memory}}}}\)

C++
void bubbleSort(vector<pair<int, int> > &suit) { /// sap xep theo ")" tang dan
    int len = suit.size();
    for (int i = 0; i + 1 < len; ++i)
        for (int j = i + 1; j < len; ++j)
            if (suit[i].second > suit[j].second) /// neu 2 cap khong dung vi tri thi dao lai
                swap(suit[i], suit[j]);
}

  • Hàm \(encode()\) để mã hóa sang xâu \(w\)

Làm mới mảng \(w\)

Duyệt từng cặp \(barrack\) và đếm xem có bao nhiêu phần tử \(")"\) thì vứt vào dãy \(w\)

\(_{_{\color{purple}{\text{Complexity : }} O(n ^ 2)\ \color{purple}{\text{auxiliary time}}\ ||\ O(n)\ \color{purple}{\text{auxiliary memory}}}}\)

C++
void encode(vector<pair<int, int> > suit, string s) { /// ma hoa sang day w
    w.clear();
    for (pair<int, int> barrack : suit) { /// voi moi cap tuong ung
        // cout << barrack.first << ' ' << barrack.second << endl;

        int cnt = 0;
        for (int i = barrack.first; i <= barrack.second; ++i) /// duyet qua doan nay
        {
            if (s[i] == ')') /// dem xem co bao nhieu phan tu ")"
            {
                cnt++;
            }
        }
        w.push_back(cnt); /// dua vao mang w
    }
}

\(\color{green}{\text{Preference TLE Code }}\): Approach

\(^{^{\color{purple}{\text{Complexity : }} O(2 ^ {2 \times n} \times n ^ 2)\ \color{purple}{\text{time}}\ ||\ O(n)\ \color{purple}{\text{memory}}}}\)

C++
int n;
vector<int> p, w;
bool invalid = true;

/// O(n ^ 2) time - O(n) space
void getPairs(vector<pair<int, int> > &suit, string s) { /// lay tung cap "(" va ")" tuong ung
    int n = s.size();
    vector<int> pos(n); /// Vi tri cua no trong xau ban dau
    for (int i = 0; i < n; ++i)
        pos[i] = i + 1; /// Khoi tao vi tri dau tien

    for (int i = 0; i + 1 < n; ++i)
    {
        if (s[i] == '(' && s[i + 1] == ')') /// Neu tim thay mot cap tuong ung
        {
            suit.push_back(make_pair(pos[i], pos[i + 1])); /// them cap do vao mang
            s.erase(i, 2); /// xoa phan tu di

            for (int j = i; j + 2 < n; ++j) /// don mang len 2 don vi
                pos[j] = pos[j + 2];

            pos.resize(s.size());

            i = -1; /// duyet lai tu dau
            // cout << s << endl; /// debug
        }
    }
}

/// O(n ^ 2) time - O(1) space
void bubbleSort(vector<pair<int, int> > &suit) { /// sap xep theo ")" tang dan
    int len = suit.size();
    for (int i = 0; i + 1 < len; ++i)
        for (int j = i + 1; j < len; ++j)
            if (suit[i].second > suit[j].second) /// neu 2 cap khong dung vi tri thi dao lai
                swap(suit[i], suit[j]);
}

/// O(n ^ 2) time - O(n) space
void encode(vector<pair<int, int> > suit, string s) { /// ma hoa sang day w
    w.clear();
    for (pair<int, int> barrack : suit) { /// voi moi cap tuong ung
        // cout << barrack.first << ' ' << barrack.second << endl;

        int cnt = 0;
        for (int i = barrack.first; i <= barrack.second; ++i) /// duyet qua doan nay
        {
            if (s[i] == ')') /// dem xem co bao nhieu phan tu ")"
            {
                cnt++;
            }
        }
        w.push_back(cnt); /// dua vao mang w
    }
}

/// O(n ^ 2) time - O(n) space
void update(string s) { /// cap nhat lai day w
    invalid = false; /// danh dau da tim thay

    vector<pair<int, int> > suit; /// mang cac cap tuong ung
    getPairs(suit, s); /// tim cac cap tuong ung
    bubbleSort(suit);  /// sap xep cac cap tuong ung
    encode(suit, s);   /// ma hoa cac cap tuong ung
}

/// O(n ^ 2) time - O(n) space
bool check(string s) { /// kiem tra xau s co ma hoa ra day p hay khong
    vector<int> current_p; /// mang ma hoa p hien tai
    for (int cur = 0; cur < s.size(); ++cur)
    {
        if (s[cur] == ')') /// neu ki tu hien tai la ")"
        {
            int cnt = 0;
            for (int pre = 0; pre < cur; ++pre)
            {
                if (s[pre] == '(') /// dem so luong phan tu "("
                {
                    cnt++;
                }
            }
            current_p.push_back(cnt); /// them phan tu do vao mang p hien tai
        }
    }
    return current_p == p; /// neu mang p hien tai la mang p ma hoa duoc thi return true
}

/// O(2 ^ (2 * n) * n ^ 2) time - O(n) space
void build(string s = "") {
    if (s.size() == 2 * n) /// Da build xong xau s
    {
        if (check(s)) update(s); /// Neu kiem tra xau s thoa man thi cap nhat day w
        return ;
    }

    build(s + '('); /// thu xay them dau ngoac mo
    build(s + ')'); /// thu xay them dau ngoac dong
}

/// O(2 ^ (2 * n) * n ^ 2) time - O(n) space
int main()
{
    /// Nhan mang p
    n = readInt();
    p.resize(n);
    for (int &x : p) cin >> x;

    /// Xu li
    build();

    /// Xuat ket qua
    if (invalid) /// neu khong tim thay
    {
        cout << -1;
        return 0;
    }
    for (int x : w) cout << x << ' '; /// neu tim thay
    return 0;
}

\(\color{orange}{\text{Hint 2 <Branch-and-bound>}}\)

  • Chúng ta sẽ cải thiện thuật toán bằng cách loại bỏ các trường hợp không cần thiết

  • Nhận xét rằng xâu ta cần xử lí là một dãy ngoặc đúng, vì thế ta không cần tạo các dãy vô nghĩa

  • Nhận xét rằng dùng biến global, reference kiểu mảng thì tốt hơn là phải clone mảng và thêm phần tử rồi xử lí tiếp mảng đó

  • Nhận xét rằng trong phần kiểm tra ta có thể dùng kết quả trước để tính tiếp phần sau

  • Nhận xét rằng việc tìm các cặp tương ứng có thể đổi sang thuật toán khác để không cần tạo thêm mảng

  • Nhận xét rằng việc sắp xếp có thể cải thiện bằng thuật toán tốt hơn và đặc biệt độ dài \(s \leq 2 \times n\)

  • Nhận xét rằng việc mã hóa sang xâu \(w\) có thể được cải thiện bằng quy hoạch động tổng tiền tố

  • Nhận xét rằng chỉ cần một dãy \(w\) tim được là xuất ra luôn, khi này ta dừng ngay chương trình lại


\(\color{goldenrod}{\text{Approach <Branch-and-bound>}}\)

  • Hàm \(build()\) ta gọi \(diff\) là độ chênh lệch giữa số lượng \("("\)\(")"\)

Nếu \((diff \geq 2 \times n - |s|)\), khi ta thêm \("("\) thì sau đó khi \(|s| = n\) số lượng \("("\) sẽ lớn áp đảo \(")"\) hay \(diff > 0\)

Nếu \((diff \leq 0)\), khi ta thêm \(")"\) thì nó sẽ không có cặp tương ứng

\(_{_{\color{purple}{\text{Complexity : }} O(catalan(n) \times n ^ 2)\ \color{purple}{\text{auxiliary time}}\ ||\ O(1)\ \color{purple}{\text{auxiliary memory}}}}\)

C++
/// O(catalan(n) * n ^ 2) time - O(1) space
void build(int diff = 0) {
    if (s.size() == 2 * n) /// Da build xong
    {
        if (check()) update();
        return ;
    }

    if (diff < 2 * n - s.size()) /// Neu them '(' thi cuoi cung diff > 0
    {
        s += '(';        /// <- them o day
        build(diff + 1); /// <- xu li o day
        s.pop_back();    /// <- nho xoa o day
    }

    if (diff > 0) /// Neu them ')' thi khong co ton tai '(' tuong ung
    {
        s += ')';        /// <- them o day
        build(diff - 1); /// <- xu li o day
        s.pop_back();    /// <- nho xoa o day
    }
}

  • Hàm \(check()\) ta cải tiến như sau

Gọi \(last\) là vị trí cuối cùng đã xét

Gọi \(val\) là giá trị đếm được tính tới hiện tại

Gọi \(index\) là vị trí của giá trị \(p\)

Gọi \(cur\) là vị trí hiện tại mình đang xét

Khi gặp giá trị \(")"\)

Ta chỉ cần cập nhật tiếp từ vị trí \(last\) tăng giá trị \(val\)

Nếu \(val \neq p[index]\) thì \(s\) không thể mã hóa ra xâu \(p\)

Duyệt tới vị trí \(index + 1\) tiếp theo và gán \(last\) là giá trị hiện tại

\(_{_{\color{purple}{\text{Complexity : }} O(n)\ \color{purple}{\text{auxiliary time}}\ ||\ O(1)\ \color{purple}{\text{auxiliary memory}}}}\)

C++
bool check() {
    int last = 0, val = 1, index = 0;
    for (int cur = 0; cur < s.size(); ++cur)
    {
        if (s[cur] == ')')
        {
            for (int pre = last + 1; pre < cur; ++pre) /// Tiep tuc dem '('
                if (s[pre] == '(')
                    val++; /// Gia tri dem duoc hien tai

            if (val != p[index++]) return false; /// Khong the ma hoa ra xau p
            last = cur; /// Cap nhat vi tri cuoi
        }
    }

    return true;
}

  • Hàm \(getPairs()\) ta cải tiến như sau

Gọi \(pre\) là vị trí trái đang xét

Gọi \(cur\) là vị trí phải đang xét

Độ dài xâu \(s\)\(2 \times n\) nên mình chỉ cần duyệt \(n\) lần lấy cặp

Nếu \(s[pre] + s[cur] = "()"\)

Ta sẽ gán \(s[pre] = s[cur] = useless\) với giá trị vô nghĩa \(useless \neq "("\)\(useless \neq ")"\)

Ta thêm cặp \((pre, cur)\) vào mảng \(suit\)

Đưa con trỏ \(pre\) sang trái tới khi giá trị hiện tại không còn vô nghĩa

Đưa con trỏ \(cur\) sang phải tới khi giá trị hiện tại không còn vô nghĩa

Còn trong trường hợp còn lại ta đưa \(pre, cur\) là sang phải tới khi gặp phần tử không vô nghĩa \(\Rightarrow (pre, cur) \rightarrow (cur, nxt)\)

\(_{_{\color{purple}{\text{Complexity : }} O(n)\ \color{purple}{\text{auxiliary time}}\ ||\ O(1)\ \color{purple}{\text{auxiliary memory}}}}\)

C++
void getPairs(vector<pair<int, int> > &suit) {
    int pre = 0, cur = 1;
    while (suit.size() < n) /// co n cap tat ca
    {
        if (s[pre] == '(' && s[cur] == ')') {
            s[pre] = s[cur] = '|'; /// gan gia tri vo nghia
            suit.push_back(make_pair(pre + 1, cur + 1)); /// tim duoc cap tuong ung
            while (s[pre] == '|') pre--; /// dua con tro sang trai
            while (s[cur] == '|') cur++; /// dua con tro sang phai
        }
        else {
            int nxt = cur + 1;
            while (s[nxt] == '|') nxt++; /// tim vi tri tiep theo
            tie(pre, cur) = make_tuple(cur, nxt); /// (pre, cur) = (cur, nxt)
        }
    }
}

  • Hàm \(countSort()\) ta làm như sau

Vì mỗi phần tử vị trí \(")"\) phân biệt, ta sẽ lưu giá trị tại vị trí \(")"\) là vị trí \("("\)

Duyệt qua từng giá trị tại các vị trí tăng dần, ta sẽ thêm vào mảng \(suit\) một cặp vị trí (\("("\)\(")"\)) mới tăng dần

\(_{_{\color{purple}{\text{Complexity : }} O(n)\ \color{purple}{\text{auxiliary time}}\ ||\ O(n)\ \color{purple}{\text{auxiliary memory}}}}\)

C++
void countSort(vector<pair<int, int> > &suit) {
    vector<int> leftval(2 * n + 1, 0);
    for (int i = 0; i < n; ++i)
        leftval[suit[i].second] = suit[i].first; /// luu vi tri '(' vao vi tri cua ')'

    suit.clear(); /// xoa mang
    for (int right = 1; right < leftval.size(); ++right) /// duyet tung vi tri ')' tang dan
        if (leftval[right] != 0)
            suit.push_back(make_pair(leftval[right], right)); /// them cap vi tri tuong ung ('(', ')') tang dan
}

  • Hàm \(encode()\) ta làm như sau

Gọi \(f[n]\) là số lượng \(")"\) trong đoạn \([1, n]\)

Với \(f[0] = 0\) ta tính được \(f[l..r] = f[r] - f[l - 1]\)

Gọi \(last\) là vị trí cuối cùng xét được

Khi mã hóa thêm một cặp \((left, right)\) mới

Ta cập nhật đoạn \(f[last + 1..right - 1] = f[last]\)

Và ta có vị trí \(f[right] = f[last] + 1\)

Ta thêm giá trị \(f[right] - f[left - 1]\) vào dãy \(w\)

Cập nhật giá trị \(last\)

\(_{_{\color{purple}{\text{Complexity : }} O(n)\ \color{purple}{\text{auxiliary time}}\ ||\ O(1)\ \color{purple}{\text{auxiliary memory}}}}\)

C++
void encode(vector<pair<int, int> > suit) {
    vi f(2 * n + 2, 0); /// f[n] = so luong ')' trong doan [1..n]
    int last = 0;
    for (int cur = 0; cur < n; ++cur) {
        int left  = suit[cur].first;
        int right = suit[cur].second;
        for (int j = last + 1; j < right; ++j) /// doan nay chua co them phan tu ')'
            f[j] = f[last];

        f[right] = f[right - 1] + 1; /// them ohan tu ')' vao
        w.push_back(f[right] - f[left - 1]); /// ma hoa duoc mot phan tu vao day w
        last = right; /// cap nhat gia tri cuoi
    }

    for (int x : w) cout << x << ' ';
    exit(0);
}

\(\color{green}{\text{Preference TLE Code }}\): Branch-and-bound

\(^{^{\color{purple}{\text{Complexity : }} O(catalan(n) \times n ^ 2)\ \color{purple}{\text{time}}\ ||\ O(n)\ \color{purple}{\text{memory}}}}\)

C++
int n;
vector<int> p, w;
string s;

/// O(n ^ 2) time - O(1) space
void getPairs(vector<pair<int, int> > &suit) {
    int pre = 0, cur = 1;
    while (suit.size() < n) /// co n cap tat ca
    {
        if (s[pre] == '(' && s[cur] == ')') {
            s[pre] = s[cur] = '|'; /// gan gia tri vo nghia
            suit.push_back(make_pair(pre + 1, cur + 1)); /// tim duoc cap tuong ung
            while (s[pre] == '|') pre--; /// dua con tro sang trai
            while (s[cur] == '|') cur++; /// dua con tro sang phai
        }
        else {
            int nxt = cur + 1;
            while (s[nxt] == '|') nxt++; /// tim vi tri tiep theo
            tie(pre, cur) = make_tuple(cur, nxt); /// (pre, cur) = (cur, nxt)
        }
    }
}

/// O(n) time - O(1) space
void encode(vector<pair<int, int> > suit) {
    vi f(2 * n + 2, 0); /// f[n] = so luong ')' trong doan [1..n]
    int last = 0;
    for (int cur = 0; cur < n; ++cur) {
        int left  = suit[cur].first;
        int right = suit[cur].second;
        for (int j = last + 1; j < right; ++j) /// doan nay chua co them phan tu ')'
            f[j] = f[last];

        f[right] = f[right - 1] + 1; /// them ohan tu ')' vao
        w.push_back(f[right] - f[left - 1]); /// ma hoa duoc mot phan tu vao day w
        last = right; /// cap nhat gia tri cuoi
    }

    for (int x : w) cout << x << ' ';
    exit(0);
}

/// O(n) time - O(n) space
void countSort(vector<pair<int, int> > &suit) {
    vector<int> leftval(2 * n + 1, 0);
    for (int i = 0; i < n; ++i)
        leftval[suit[i].second] = suit[i].first; /// luu vi tri '(' vao vi tri cua ')'

    suit.clear(); /// xoa mang
    for (int right = 1; right < leftval.size(); ++right) /// duyet tung vi tri ')' tang dan
        if (leftval[right] != 0)
            suit.push_back(make_pair(leftval[right], right)); /// them cap vi tri tuong ung ('(', ')') tang dan
}

/// O(n ^ 2) time - O(1) space
void update() {
    vector<pair<int, int> > suit;
    getPairs(suit);
    countSort(suit);
    encode(suit);
}

/// O(n) time - O(1) space
bool check() {
    int last = 0, val = 1, index = 0;
    for (int cur = 0; cur < s.size(); ++cur)
    {
        if (s[cur] == ')')
        {
            for (int pre = last + 1; pre < cur; ++pre) /// Tiep tuc dem '('
                if (s[pre] == '(')
                    val++; /// Gia tri dem duoc hien tai

            if (val != p[index++]) return false; /// Khong the ma hoa ra xau p
            last = cur; /// Cap nhat vi tri cuoi
        }
    }

    return true;
}

/// O(catalan(n) * n ^ 2) time - O(1) space
void build(int diff = 0) {
    if (s.size() == 2 * n) /// Da build xong
    {
        if (check()) update();
        return ;
    }

    if (diff < 2 * n - s.size()) /// Neu them '(' thi cuoi cung diff > 0
    {
        s += '(';        /// <- them o day
        build(diff + 1); /// <- xu li o day
        s.pop_back();    /// <- nho xoa o day
    }

    if (diff > 0) /// Neu them ')' thi khong co ton tai '(' tuong ung
    {
        s += ')';        /// <- them o day
        build(diff - 1); /// <- xu li o day
        s.pop_back();    /// <- nho xoa o day
    }
}

/// O(catalan(n) * n ^ 2) time - O(n) space
int main()
{
    n = readInt();
    p.resize(n);
    for (int &x : p) cin >> x;

    build();
    cout << -1;
    return 0;
}


Bình luận


  • 2
    SPyofgame    3:43 p.m. 16 Tháng 7, 2020

    Mình sẽ thêm phần full (\(<Data-structure>\)) sau UwU


    • 3
      cuom1999    12:49 p.m. 16 Tháng 7, 2020

      SPyofgame, Preference code mà sao \(O(n^2)\) được em nhỉ

      1 phản hồi

      • 0
        vô_nhiễm    11:37 a.m. 16 Tháng 7, 2020

        hình như bài này có tồn tại cách làm O(n) thì phải

        1 phản hồi

        • -3
          todonghai2k7    11:00 a.m. 16 Tháng 7, 2020

          Ghê quá UwU


          • 2
            SPyofgame    10:39 a.m. 16 Tháng 7, 2020

            Mình sẽ thêm phần \(<Branch-and-bound>, <Data-structure>\) sau UwU