Hướng dẫn giải của [KSCUM VT 02] CÂU 3: Mật độ xuất hiện cao
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.
Hướng dẫn giải
Đoạn con X có mật độ cao khi tồn tại một chữ cái c mà số lần xuất hiện của c lớn hơn số ký tự còn lại, tức là 2 \cdot cnt_c > độ dài. Cần tìm độ dài lớn nhất của các đoạn như vậy.
Sub 1: Vét cạn — ~O(26 \cdot n^2)~, dùng khi ~n \le 2000~
Với mỗi điểm đầu l, mở rộng r và đếm số lần xuất hiện của từng chữ cái. Với mỗi đoạn [l, r], kiểm tra xem có chữ cái nào thỏa mãn 2 \cdot cnt > độ dài. Nếu chỉ nộp sub này: đúng 12/20 test.
Sub 2: Tiền tố — ~O(26 \cdot n)~
Với mỗi chữ cái c, đặt vi = 2 \cdot (số lần c trong i ký tự đầu) - i. Đoạn (p, r] có mật độ cao khi vr > vp. Vì v thay đổi đúng 1 mỗi bước và v0 = 0, nếu vr > 0 thì p = 0 là tốt nhất; nếu vr <= 0 thì p nhỏ nhất là lần đầu tiên v đạt giá trị v_r - 1. Lưu vị trí xuất hiện đầu tiên của mỗi giá trị v để tra cứu. Sub 2 đúng 20/20 test.
Chương trình hoàn chỉnh
Hàm main đọc chuỗi S. Nếu n \le 2000 thì gọi sub 1, còn lại gọi sub 2.
#include <bits/stdc++.h>
using namespace std;
string s; // chuỗi S, đánh số từ 1
int n;
// Đoạn [l, r] có mật độ cao <=> tồn tại ký tự c mà 2 * cnt_c(l, r) > (r - l + 1)
// Sub 1: vét cạn O(26 * n^2) — thử mọi đoạn [l, r], đếm số lần xuất hiện của từng chữ cái.
// Dùng khi n <= 2000.
void sub1() {
int best = 0;
for (int l = 0; l < n; l++) {
int cnt[26] = {0};
for (int r = l; r < n; r++) {
cnt[s[r] - 'a']++;
int len = r - l + 1;
for (int c = 0; c < 26; c++)
if (2 * cnt[c] > len) {
best = max(best, len);
break;
}
}
}
cout << best << "\n";
}
// Sub 2: O(26 * n). Với mỗi ký tự c, đặt v_i = 2 * (số lần c trong i ký tự đầu) - i.
// Đoạn (p, r] có mật độ cao nếu v_r > v_p. Vì v thay đổi mỗi bước đúng ±1 và v_0 = 0:
// - nếu v_r > 0 thì p = 0 là tốt nhất, độ dài r;
// - nếu v_r <= 0 thì p nhỏ nhất là lần đầu tiên v đạt giá trị v_r - 1.
void sub2() {
int best = 0;
const int OFF = n + 1; // dịch chỉ số âm
vector<int> first(2 * n + 5);
for (int c = 0; c < 26; c++) {
fill(first.begin(), first.end(), -1);
int v = 0;
first[v + OFF] = 0;
for (int r = 1; r <= n; r++) {
v += (s[r - 1] - 'a' == c) ? 1 : -1;
if (v > 0) {
best = max(best, r);
} else if (first[v - 1 + OFF] != -1) {
best = max(best, r - first[v - 1 + OFF]);
}
if (first[v + OFF] == -1) first[v + OFF] = r;
}
}
cout << best << "\n";
}
int main() {
freopen("CAU3.INP", "r", stdin);
freopen("CAU3.OUT", "w", stdout);
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> s;
n = s.size();
if (n <= 2000) sub1(); // dữ liệu nhỏ: vét cạn cho chắc
else sub2();
return 0;
}
Bình luận