Hướng dẫn giải của Tiền tố và hậu tố
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ậpTác giả: , , ,
Editorial for suprefix: Tiền tố và hậu tố
Hiểu bài toán
Cho hai xâu a và b. Tìm xâu c ngắn nhất sao cho a là tiền tố của c và b là hậu tố của c. Điều này có nghĩa là a phải nằm ở đầu c và b phải nằm ở cuối c. Để c có độ dài ngắn nhất, ta cần chồng lấp a và b càng nhiều càng tốt. Cụ thể, ta cần tìm độ dài lớn nhất của một đoạn hậu tố của a trùng với một đoạn tiền tố của b. Gọi độ dài này là k. Khi đó, c sẽ là a nối với phần còn lại của b (tức là b bị thiếu mất k ký tự đầu).
Các cách tiếp cận
Cách Dùng mảng LPS (KMP)
#include <bits/stdc++.h>
using namespace std;
vector<int> build_lps(const string &s) {
int n = s.size();
vector<int> lps(n, 0);
for (int i = 1; i < n; i++) {
int j = lps[i - 1];
while (j > 0 && s[i] != s[j]) {
j = lps[j - 1];
}
if (s[i] == s[j]) {
j++;
}
lps[i] = j;
}
return lps;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
string a, b;
if (!(cin >> a>>b)) return 0;
string s=b+"#"+a;
vector<int> lps=build_lps(s);
int k=lps[s.size()-1];
string c = a + b.substr(k);
cout << c;
return 0;
}
- Time Complexity: O(|a| + |b|)
- Space Complexity: O(|a| + |b|)
Đây là phương pháp ótimal và hiệu quả nhất.
- Tạo xâu kết hợp: Tạo một xâu mới
s = b + '#' + a. Ký tự đặc biệt#dùng để ngăn cáchbvàa, đảm bảo rằng độ dài chung tìm được không vượt quá độ dài củab. - Xây dựng mảng LPS: Áp dụng thuật toán Knuth-Morris-Pratt (KMP) để xây dựng mảng LPS cho xâu
s. Mảnglps[i]lưu độ dài lớn nhất của tiền tố củasđồng thời là hậu tố củasending tạii. - Tìm độ dài chồng lấp: Giá trị
lps[s.size() - 1]chính là độ dài lớn nhất của tiền tố củab(vìbnằm ở đầus) trùng với một hậu tố củaa(vìanằm ở cuốis). - Xây dựng kết quả: Xâu
cngắn nhất làacộng với phần còn lại củab. Phần còn lại này làbbị cắt bỏkký tự đầu, tức làb.substr(k). Nối lại ta đượcc = a + b.substr(k).
Cách Brute Force (Tối ưu)
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
int main() {
string a, b;
cin >> a >> b;
int n = a.length();
int m = b.length();
int max_overlap = 0;
// Chỉ kiểm tra các độ dài chồng lấp có thể
// Độ dài chồng lấp không thể lớn hơn min(n, m)
int limit = min(n, m);
for (int k = 1; k <= limit; ++k) {
// Kiểm tra xem k ký tự cuối của a có trùng với k ký tự đầu của b không
// a.substr(n - k) so sánh với b.substr(0, k)
if (a.substr(n - k, k) == b.substr(0, k)) {
max_overlap = k;
}
}
// Nếu không có chồng lấp (max_overlap = 0), kết quả là a + b
// Nếu có chồng lấp, a đã chứa phần đầu của b, chỉ cần nối phần còn lại
cout << a << b.substr(max_overlap) << endl;
return 0;
}
- Time Complexity: O(min(|a|, |b|)^2)
- Space Complexity: O(|a| + |b|)
Phương pháp này kiểm tra trực tiếp các khả năng chồng lấp.
- Vòng lặp: Duyệt qua tất cả các độ dài
ktừ 1 đến độ dài ngắn hơn củaahoặcb. - So sánh: Với mỗi
k, so sánh trực tiếpkký tự cuối củaavớikký tự đầu củab. - Lưu kết quả: Nếu trùng khớp, cập nhật
max_overlap = k. Vì ta duyệtktăng dần, lần cập nhật cuối cùng sẽ là độ dài lớn nhất. - Tạo kết quả: Cuối cùng, nối
avới phần đuôi củab(bỏ đimax_overlapký tự đầu).
Lưu ý: Trong giải pháp này, việc sử dụng substr trong vòng lặp có thể gây tốn bộ nhớ nếu không tối ưu, nhưng về lý thuyết thời gian chạy vẫn cao do số lần so sánh lớn và độ phức tạp của substr (hoặc so sánh trực tiếp từng ký tự).
Phân tích độ phức tạp
| Cách tiếp cận | Time | Space | Tên | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | O( | a | + | b | ) | O( | a | + | b | ) | Dùng mảng LPS (KMP) |
| 2 | O(min( | a | , | b | )^2) | O( | a | + | b | ) | Brute Force (Tối ưu) |
Bài học kinh nghiệm
- Bài toán quy về tìm độ dài lớn nhất của một hậu tố của
atrùng với một tiền tố củab. - Thuật toán KMP (mảng LPS) được dùng để tìm độ dài lớn nhất của tiền tố của một xâu đồng thời là hậu tố của chính nó. Bằng cách ghép
bvàalại (có ký tự ngăn cách), ta利用 được tính chất này để tìm độ dài chồng lấp. - Ký tự ngăn cách (như
#) là quan trọng để đảm bảo độ dài chung tìm được không vượt quá độ dài củab.
Lỗi thường gặp
- Quên sử dụng ký tự ngăn cách khi ghép xâu trong phương pháp KMP có thể dẫn đến việc tìm được độ dài chồng lấp lớn hơn độ dài của
b(ví dụ nếuachứabhoàn toàn). - Sử dụng thuật toán Brute Force với độ phức tạp O(N^2) có thể gây TLE (Time Limit Exceeded) với giới hạn 10^5.
- Xử lý sai trường hợp không có chồng lấp (kết quả đúng vẫn là
a + b).
Bình luận