Hướng giải của Palindrome với Hash
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: Palindrome với Hash (Hash xuôi và Hash ngược)
Phân tích
Xâu con S[l..r] là palindrome khi và chỉ khi nó bằng với xâu đảo ngược của nó. Tính hash xuôi của S và hash "ngược" của S (hash của S đảo ngược). Xâu con S[l..r] palindrome nếu hash xuôi bằng hash của xâu con ngược.
Độ phức tạp
- Thời gian: O(N + Q)
- Không gian: O(N)
Code mẫu C++
#include <bits/stdc++.h>
using namespace std;
const long long BASE = 31;
const long long MOD = 1e9 + 7;
int main() {
string s;
cin >> s;
int n = s.size();
// Dao nguoc xau S de tinh hash "nguoc"
string rs = s;
reverse(rs.begin(), rs.end());
// Tinh mang luy thua chung
vector<long long> pw(n + 1);
pw[0] = 1;
for (int i = 1; i <= n; i++) pw[i] = pw[i - 1] * BASE % MOD;
// Hash tien to cua S (xuoi) va cua rs (nguoc)
vector<long long> hf(n + 1), hb(n + 1);
for (int i = 0; i < n; i++) {
hf[i + 1] = (hf[i] * BASE + (s[i] - 'a' + 1)) % MOD; // Hash xuoi
hb[i + 1] = (hb[i] * BASE + (rs[i] - 'a' + 1)) % MOD; // Hash nguoc
}
// Ham lay hash cua doan [l, r] tu mang hash h
auto getHash = [&](vector<long long>& h, int l, int r) {
return (h[r + 1] - h[l] * pw[r - l + 1] % MOD + MOD) % MOD;
};
int q;
cin >> q;
while (q--) {
int l, r;
cin >> l >> r;
// Hash xuoi cua S[l..r]
long long f = getHash(hf, l, r);
// Hash nguoc cua S[l..r] = hash cua rs[n-1-r..n-1-l]
long long b = getHash(hb, n - 1 - r, n - 1 - l);
cout << (f == b ? "YES" : "NO") << endl;
}
return 0;
}
Giải thích
- Xâu
S[l..r]khi đảo ngược trở thànhS[r]S[r-1]...S[l], tương ứng với xâurs[n-1-r..n-1-l]. - Nếu hash xuôi = hash của xâu đảo thì xâu đó đọc xuôi = đọc ngược, tức là palindrome.
- Mỗi truy vấn trả lời trong O(1) nhờ hash tiền tố.
Nhận xét