Hướng dẫn giải của Mật Khẩu


Chỉ dùng lời giải này khi không có ý tưởng, và đừng copy-paste code từ lời giải này. Hãy tôn trọng người ra đề và người viết lời giải.
Nộp một lời giải chính thức trước khi tự giải là một hành động có thể bị ban.

Lời giải này đang bị ẩn cho đến khi bạn chọn mở ra.

Chúng tôi khuyên bạn nên tự thử giải bài trước. Việc mở lời giải có thể làm lộ mất ý tưởng chính trước khi bạn có cơ hội tự giải.

Bạn phải đăng nhập để mở lời giải này.

Đăng nhập

Tác giả: Hiếu Nguyễn, khaibadao, dainghiajustiin, blablablabkabcu

Hiểu bài toán

Bài toán yêu cầu sinh một chuỗi mật khẩu có độ dài exactly N. Chuỗi mật khẩu phải thỏa mãn các ràng buộc về số lượng ký tự:

  • Có đúng x ký tự 'A' và 'B' xen kẽ (bắt đầu bằng 'A')
  • Có đúng y ký tự 'a' và 'b' xen kẽ (bắt đầu bằng 'a')
  • Có đúng z ký tự '0' và '1' xen kẽ (bắt đầu bằng '0')
  • Các phần còn lại (n - x - y - z) là các ký tự '0' và '1' xen kẽ (bắt đầu bằng '0')

Điều kiện:

  • Chuỗi kết quả có độ dài exactly N
  • Các ký tự trong chuỗi được chia thành 3 loại: 'A'/'B', 'a'/'b', và '0'/'1'
  • Các chuỗi con của mỗi loại phải được tạo thành từ các ký tự xen kẽ
  • Vị trí của các chuỗi con trong chuỗi kết quả không được chỉ định rõ ràng trong đề bài, nhưng các giải pháp đang đặt chúng theo thứ tự cố định

Các cách tiếp cận

Cách Xây dựng trực tiếp theo thứ tự
#include <bits/stdc++.h>
using namespace std;

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);

    int n, x, y, z;
    cin >> n >> x >> y >> z;

    // Phần '0'/'1' còn lại
    int remaining = n - x - y - z;
    for(int i = 1; i <= remaining; i++) {
        if(i % 2 == 1) cout << '0';
        else cout << '1';
    }

    // Phần 'A'/'B'
    for(int i = 1; i <= x; i++) {
        if(i % 2 == 1) cout << 'A';
        else cout << 'B';
    }

    // Phần 'a'/'b'
    for(int i = 1; i <= y; i++) {
        if(i % 2 == 1) cout << 'a';
        else cout << 'b';
    }

    return 0;
}
  • Time Complexity: O(n)
  • Space Complexity: O(1)

Đây là cách tiếp cận đơn giản nhất. Ta chia chuỗi thành 3 phần theo thứ tự:

  1. Các ký tự '0'/'1' còn lại (sau khi đã dùng z ký tự cho phần xen kẽ với '0'/'1')
  2. Các ký tự 'A'/'B' xen kẽ
  3. Các ký tự 'a'/'b' xen kẽ

Vòng lặp for đơn giản với kiểm tra i % 2 để sinh ra ký tự xen kẽ tương ứng. Độ phức tạp là O(n) vì ta chỉ duyệt qua từng ký tự một lần.

Cách Sử dụng chuỗi lặp lại
#include <bits/stdc++.h>
using namespace std;

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);

    int n, x, y, z;
    cin >> n >> x >> y >> z;

    string result = "";

    // Thêm phần '0'/'1' còn lại
    for(int i = 0; i < n - x - y - z; i++) {
        result += (i % 2 == 0) ? '0' : '1';
    }

    // Thêm phần 'A'/'B'
    for(int i = 0; i < x; i++) {
        result += (i % 2 == 0) ? 'A' : 'B';
    }

    // Thêm phần 'a'/'b'
    for(int i = 0; i < y; i++) {
        result += (i % 2 == 0) ? 'a' : 'b';
    }

    cout << result;
    return 0;
}
  • Time Complexity: O(n)
  • Space Complexity: O(n)

Cách tiếp cận này tương tự như cách 1 nhưng sử dụng string để nối các ký tự lại. Ưu điểm là code dễ đọc hơn và có thể Debug dễ dàng. Nhược điểm là tốn thêm O(n) bộ nhớ cho string. Tuy nhiên, trong thực tế, hiệu năng vẫn tương đương với cách 1.

Cách Xử lý đặc biệt với z
#include <bits/stdc++.h>
using namespace std;

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);

    int n, x, y, z;
    cin >> n >> x >> y >> z;

    // Xử lý phần z kết hợp với remaining
    // Z là phần '0'/'1' xen kẽ bổ sung
    int total_zero_one = z + (n - x - y - z);

    for(int i = 1; i <= total_zero_one; i++) {
        if(i & 1) cout << '0';
        else cout << '1';
    }

    for(int i = 1; i <= x; i++) {
        if(i & 1) cout << 'A';
        else cout << 'B';
    }

    for(int i = 1; i <= y; i++) {
        if(i & 1) cout << 'a';
        else cout << 'b';
    }

    return 0;
}
  • Time Complexity: O(n)
  • Space Complexity: O(1)

Cách tiếp cận này nhận thấy rằng:

  • z là số lượng '0'/'1' xen kẽ bắt đầu bằng '0'
  • n - x - y - z là phần còn lại
  • Tổng số lượng '0'/'1' là z + (n - x - y - z) = n - x - y

Do đó, ta có thể gộp chúng lại và sinh ra chuỗi '0'/'1' liên tục. Phép bitwise AND (i & 1) thay thế cho i % 2 để tối ưu tốc độ.

Phân tích độ phức tạp

Cách tiếp cận Time Space Tên
1 O(n) O(1) Xây dựng trực tiếp theo thứ tự
2 O(n) O(n) Sử dụng chuỗi lặp lại
3 O(n) O(1) Xử lý đặc biệt với z

Bài học kinh nghiệm

  • Bài toán có thể được giải quyết bằng cách xây dựng chuỗi theo từng khối với thứ tự cố định: phần '0'/'1' → phần 'A'/'B' → phần 'a'/'b'
  • Z là tham số bổ sung cho phần '0'/'1' nhưng về cơ bản có thể gộp với phần còn lại để tính tổng số lượng '0'/'1'
  • Sử dụng phép bitwise AND (i & 1) để kiểm tra số chẵn/lẻ nhanh hơn phép chia lấy dư (i % 2)

Lỗi thường gặp

  • Lỗi thứ tự: Phải đảm bảo sinh ra các ký tự theo đúng thứ tự đã định (ví dụ: 'A' trước 'a')
  • Lỗi đếm chỉ số: Khi dùng chỉ số 1-based (i = 1) thì kiểm tra 'i % 2' để sinh 'A' ở vị trí lẻ
  • Quên xử lý trường hợp n - x - y - z = 0: Cần đảm bảo vòng lặp không chạy khi không cần thiết

Bình luận

Please read the guidelines before commenting.


Không có bình luận tại thời điểm này.