Hướng giải của Chu kỳ ngắn nhất của xâu (SA)
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ả:
Lời giải: Chu kỳ ngắn nhất (SA)
Phân tích
Dùng vòng lặp kiểm tra từng ước số len của n. Nếu S lặp lại n/len đoạn độ dài len thì len là chu kỳ.
Độ phức tạp
- Thời gian: O(N · số ước) = O(N log N)
- Không gian: O(N)
Code mẫu C++
#include <bits/stdc++.h>
using namespace std;
int main() {
string s;
cin >> s;
int n = s.size();
int k;
cin >> k;
vector<int> sa(n), rank(n), tmp(n);
for (int i = 0; i < n; i++) { sa[i] = i; rank[i] = s[i]; }
for (int kk = 1; kk < n; kk *= 2) {
auto cmp = [&](int a, int b) {
if (rank[a] != rank[b]) return rank[a] < rank[b];
int ra = (a + kk < n) ? rank[a + kk] : -1;
int rb = (b + kk < n) ? rank[b + kk] : -1;
return ra < rb;
};
sort(sa.begin(), sa.end(), cmp);
tmp[sa[0]] = 0;
for (int i = 1; i < n; i++)
tmp[sa[i]] = tmp[sa[i-1]] + cmp(sa[i-1], sa[i]);
rank = tmp;
}
vector<int> lcp(n, 0);
vector<int> inv(n);
for (int i = 0; i < n; i++) inv[sa[i]] = i;
int kk = 0;
for (int i = 0; i < n; i++) {
if (inv[i] == n - 1) { kk = 0; continue; }
int j = sa[inv[i] + 1];
while (i + kk < n && j + kk < n && s[i + kk] == s[j + kk]) kk++;
lcp[inv[i]] = kk;
if (kk > 0) kk--;
}
// Số xâu con phân biệt đúng độ dài k = số hậu tố có LCP với hậu tố kế trước < k
// mà bản thân hậu tố có độ dài >= k
long long count = 0;
int start = -1;
for (int i = 0; i < n; i++) {
int len = n - sa[i];
if (len < k) continue;
int prev_lcp = (i > 0) ? lcp[i - 1] : 0;
if (prev_lcp < k) count++;
}
cout << count << endl;
return 0;
}
Nhận xét