Cho hai chuỗi **needle** và **haystack**, tìm tất cả vị trí xuất hiện của **needle** trong **haystack**.
Ví dụ:
Input:
2
na
banananobano
Output:
2 4
Giải thích: "na" xuất hiện tại vị trí **2** và **4**.
🔹 Bài toán sử dụng **Hashing (Rolling Hash)** để tìm kiếm chuỗi con nhanh chóng.
Ý tưởng chính:
- **Tiền xử lý**: Tính giá trị hash của chuỗi **needle** và prefix hash của **haystack**.
- Sử dụng công thức **getHash(l, r)** để lấy hash của từng đoạn con trong **haystack**.
- So sánh hash để tìm tất cả vị trí khớp với **needle**.
Quy trình thực hiện:
- 🟢 Bước 1: Tiền xử lý mảng **Pow** (lũy thừa của base) và **Hash** (prefix hash của **haystack**).
- 🟡 Bước 2: Duyệt từng vị trí trong **haystack**, so sánh hash với **needle**.
- 🔴 Bước 3: Nếu khớp, in ra vị trí xuất hiện.
Độ phức tạp: O(n + m), nhanh hơn nhiều so với phương pháp brute-force.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define ed cout << "\n"
#define mem(a,b) memset(a, b, sizeof(a))
#define all(a) a.begin(), a.end()
#define sz(A) (int) A.size()
#define faster() ios_base::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL);
const ll MOD = 1e9 + 9;
const int base = 31;
vector<ll>Hash, Pow;
string a, b;
ll n, m, k, HashA, ok;
void __init__() {
a = ' ' + a;
b = ' ' + b;
n = a.length() - 1, m = b.length() - 1;
Hash.assign(m + 1, 0);
Pow.assign(m + 1, 0);
Pow[0] = 1; HashA = 0; ok = 0;
for(int i = 1; i <= m; i++) {
Pow[i] = (Pow[i - 1] * base) % MOD;
Hash[i] = (Hash[i - 1] * base + (b[i] - 'a' + 1)) % MOD;
}
for(int i = 1; i <= n; i++) {
HashA = (HashA * base + (a[i] - 'a' + 1)) % MOD;
}
}
ll getHash(int l, int r) {
return (Hash[r] - Hash[l - 1] * Pow[r - l + 1] % MOD + MOD) % MOD;
}
void solve() {
while(cin >> n) {
cin >> a >> b;
__init__();
for(int i = 1; i <= m - n + 1; i++) {
if(getHash(i, i + n - 1) == HashA) {
cout << i - 1 << endl;
ok = 1;
}
}
if(!ok) ed;
}
}
int main(){
faster();
solve();
return 0;
}