Hướng giải của Đoạn Con Không Trùng Lặp Dài Nhất
Nhớ rằng hướng dẫn giải này chỉ nên sử dụng khi bế tắc, và tuyệt đối không nên sao chép mã nguồn kèm theo. Hãy tôn trọng tác giả bài tập và người viết hướng dẫn giải.
Nộp mã nguồn lời giải chính thức trước khi giải bài tập đó có thể khiến bạn bị ban.
Nộp mã nguồn lời giải chính thức trước khi giải bài tập đó có thể khiến bạn bị ban.
Tác giả:
Hướng dẫn giải
Bài toán yêu cầu tìm độ dài của đoạn con liên tiếp dài nhất sao cho không có phần tử nào trùng lặp. Ta áp dụng kỹ thuật Sliding Window (hoặc Two Pointers) kết hợp cấu trúc dữ liệu Map để lưu vị trí xuất hiện gần nhất của từng phần tử:
- Duy trì hai con trỏ
lvàrđại diện cho cửa sổ hiện tại \([l, r]\). - Di chuyển con trỏ
rtừ \(0\) đến \(N - 1\). Với mỗi phần tử \(a[r]\):- Nếu \(a[r]\) đã từng xuất hiện trước đó tại vị trí
pos[a[r]]và vị trí đó \(\ge l\), ta phải co cửa sổ lại bằng cách dịch con trỏ trái:l = pos[a[r]] + 1. - Cập nhật vị trí xuất hiện mới nhất của \(a[r]\) là
r. - Độ dài của đoạn con không trùng lặp kết thúc tại
rlà \(r - l + 1\). Cập nhật độ dài cực đại tìm được.
- Nếu \(a[r]\) đã từng xuất hiện trước đó tại vị trí
Độ phức tạp thời gian: \(O(N \log N)\) nếu dùng std::map hoặc \(O(N)\) trung bình nếu dùng std::unordered_map.
Mã nguồn C++ mẫu
#include <bits/stdc++.h>
using namespace std;
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
int n;
if (!(cin >> n)) return 0;
vector<int> a(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
}
// pos[x] lưu vị trí xuất hiện gần nhất của phần tử x
unordered_map<int, int> pos;
int max_len = 0;
int l = 0;
for (int r = 0; r < n; r++) {
if (pos.count(a[r])) {
// Dịch con trỏ trái để đảm bảo không chứa phần tử lặp
l = max(l, pos[a[r]] + 1);
}
pos[a[r]] = r;
max_len = max(max_len, r - l + 1);
}
cout << max_len << "\n";
return 0;
}
Nhận xét