# **Hash (Rabin–Karp)** ## 1. Bài toán: Cho xâu $A$ và xâu $B$ chỉ gồm các chữ cái thường. Xâu $B$ được gọi là xuất hiện trong vị trí $i$ của xâu $A$ nếu $A$~i~ $=$ $B$~1~, $A$~i+1~ $=$ $B$~2~,..., $A$~i+length(B)-1~ $=$ $B$~length(B)~. Hãy tìm tất cả vị trí mà $B$ xuất hiện trong $A$. ***Input:*** * Dòng 1: xâu $A$. * Dòng 2: xâu $B$. ***Output:*** * Ghi ra các vị trí tìm được trên dòng (thứ tự tăng dần). Nếu $B$ không xuất hiện trong $A$ thì bỏ trắng. ***Sample input:*** ``` aaaaa aa ``` ***Sample output:*** ``` 1 2 3 4 ``` ## 2. Thuật toán ngây thơ ```c=1 #include <bits/stdc++.h> using namespace std; #define ll long long int main() { string a, b; cin >> a >> b; int n = a.size(), m = b.size(); for(int i = 0; i < n - m + 1; i++) { string x = a.substr(i, m); if(b == x) cout << i + 1 << " "; } return 0; } ``` **Độ phức tạp thời gian: $O(n * m)$**. ## 3. Thuật toán Hash Ta sẽ biểu diễn một xâu dưới dạng một số của hệ cơ số thay vì chữ cái thông thường. Ví dụ $string$ $a$ $=$ "$abbz$", viết ở dạng số sẽ là $[1, 2, 2, 26]$, ta sẽ biểu diễn dưới dạng một hệ cơ số $base$ với $base$ $>$ $26$.Vậy, hai xâu bằng nhau khi biểu diễn của hai xâu ở hệ cơ số 10 giống nhau. Tư tưởng của thuật toán là đổi từ hệ cơ số $base$ sang hệ cơ số 10 rồi so sánh ở hệ cơ số 10 nên cũng có khả năng nằm ngoài phạm vi lưu trữ của máy tính. Để khắc phục điều này ta sẽ lấy dư cho $MOD$ với $MOD$ $=$ $10^9 + 7$. $-$ Cơ số 10 là các số $∈$ $[0 ; 9]$: * Ví dụ: $12345$ = $1×10^4 + 2×10^3 + 3×10^2 + 4×10^1 + 5×10^0$ $-$ Cơ số 2 là các số $∈$ $[0 ; 1]$: * Ví dụ: $11011$ $-$ Các hệ cơ số còn lại cũng tương tự. ### 3.1. Tính hash của b Giả sử ta có xâu $b$ = "$abc$" (chọn $base$ = 300). Vậy ở dạng số thì xâu $b$ sẽ là "$97$ $98$ $99$", Hash của $b$ $=$ $(97×300^2 + 98×300^1 + 99×300^0)$ $=$ hash($b$~1~...$b$~m~) $=$ ($b$~1~ $×$ $base$^m-1^ $+$ $b$~2~$× base$^m-2^ $+$ ... $+$ $b$~m-1~$~×base^1$ $+$ $b$~m~). ### 3.2. Tính pow và sum ```c=1 ll POW[1000009], sum[1000009]; POW[0] = 1; for(int i = 1; i <= m; i++) { POW[i] = (POW[i - 1] * base) % MOD; } sum[0] = 0; for(int i = 1; i <= n; i++) { sum[i] = (sum[i - 1] * base + a[i]) % MOD; } ``` ### 3.3. Implement ```c=1 // author : hatakaze (a.k.a Tran Gia Huy) #include "bits/stdc++.h" using namespace std; #define all(x) x.begin(), x.end() #define sz(x) (int)x.size() #define ll long long const ll MOD = 1e9 + 7; string a, b; const long long maxn = 1e6 + 9; const ll base = 300; ll POW[1000009], sum[1000009]; ll getHash(int i, int j) { return ((sum[j] - sum[i - 1] * POW[j - i + 1] + MOD * MOD) % MOD); } void solve() { cin >> a >> b; int n = sz(a), m = sz(b); a = " " + a; b = " " + b; ll HashB = 0; for(int i = 1; i <= m; i++) { HashB = (HashB * base + b[i]) % MOD; } POW[0] = 1; for(int i = 1; i <= m; i++) { POW[i] = (POW[i - 1] * base) % MOD; } sum[0] = 0; for(int i = 1; i <= n; i++) { sum[i] = (sum[i - 1] * base + a[i]) % MOD; } for(int i = 1; i <= n - m + 1; i++) { if(getHash(i, i + m - 1) == HashB) { cout << i << " "; } } } int main() { ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0); int t = 1; while(t--) solve(); return 0; } ``` **Tài liệu tham khảo:** * https://vnoi.info/wiki/algo/string/hash.md