Chuỗi AB¶
Bài toán¶
Yêu cầu:
Liệt kê các chuỗi có độ dài n gồm hai ký tự 'A' và 'B', trong đó không có hai ký tự 'B' nào đứng cạnh nhau.
Đầu vào:
Một dòng ghi số nguyên \(n (n \le 28)\) là số ký tự của một chuỗi.
Đầu ra:
Các chuỗi AB hợp lệ theo thứ tự từ điển.
Ví dụ:
| Đầu vào | Đầu ra |
|---|---|
| 3 | AAA AAB ABA BAA BAB |
Cách giải đề xuất¶
Ý tưởng chính¶
1. Sử dụng kỹ thuật quay lui đối với hai ký tự A và B.
2. Gọi \(a(n)\) là số chuỗi hợp lệ gồm \(n\) ký tự. Có hai trường hợp:
Trường hợp 1: chuỗi bắt đầu bằng A
Điều này có nghĩa là, phần còn lại gồm n - 1 ký tự và vẫn phải hợp lệ (không có hai B kề nhau).
Như vậy, số chuỗi của trường hợp này là \(a(n - 1)\).
Trường hợp 2: chuỗi bắt đầu bằng B
Vì hai B không thể kề nhau nên chuỗi có dạng BA*.
Điều này có nghĩa là, phần còn lại gồm n - 2 ký tự và vẫn phải hợp lệ.
Như vậy, số chuỗi của trường hợp này là \(a(n - 2)\).
Do đó, tổng số chuỗi hợp lệ là: \(a(n) = a(n - 1) + a(n - 2)\).
Công thức này chính là định nghĩa của dãy Fibonacci.
Điểm khác so với Fibonacci là các phần tử đầu.
- Với \(n = 1, a(n) = a(1) = 1\). Đó là các chuỗi:
A,B - Với \(n = 2, a(n) = a(2) = 3\). Đó là các chuỗi:
AA,AB,BA
3. Với \(n = 28\), ta dễ dàng tính được số chuỗi hợp lệ là: \(a(25) = 832,040\).
Mà mỗi chuỗi có \(n + 1 = 28 + 1 = 29\) ký tự, gồm 28 ký tự A hoặc B và ký tự xuống dòng, tương đương \(29\) byte.
Do đó, với \(n = 28\), dung lượng đầu ra là \(29 \times 832,040 = 24,129,160\) byte \(\approx 23 MB\).
4. Những việc tính toán trên là để hiểu rõ về đầu ra. Còn trong chương trình này, ta thực hiện theo hướng:
- Dùng kiểu
char[]để ghi chuỗi tạm nhanh hơn, tránh bị các chi phí phụ (overhead) củastring. - Biến
resultsẽ đóng vai trò là bộ đệm tạm thời (buffer tạm), nó không lưu tất cả chuỗi hợp lệ mà chỉ lưu nhiều chuỗi trong giới hạn trước khi ghi. - Mỗi khi bộ đệm đầy thì dùng
fwrite()để ghi ra tập tin.
Cách này phù hợp với đầu ra có dung lượng lớn vì giảm được số lần ghi tập tin và tiết kiệm bộ nhớ RAM.
Viết chương trình¶
0. Khai báo các biến liên quan.
biến s kiểu mảng, chứa tối đa 29 ký tự, dùng để lưu một chuỗi cặp ngoặc hợp lệ.
1. Viết hàm flush_buffer() để đẩy hết bộ đệm ra tập tin output.
2. Viết hàm backtracking() để phát sinh một chuỗi hợp lệ s.
Sau khi s được phát sinh hoàn chỉnh, ta đặt ký tự \0 vào cuối mảng để kết thúc chuỗi, nhằm ghi được ra tập tin output.
Ngoài tham số i, hàm dùng thêm tham số previous để kiểm soát việc hai ký tự B đứng cạnh nhau.
3. Gọi hàm backtracking() ra thực hiện.
Mã nguồn¶
Code đầy đủ được đặt tại GitHub.