matbensch

Rolling Hash C++ Implementation

Oct 26th, 2023
890
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 3.33 KB | None | 0 0
  1. #include <iostream>
  2. #include <string>
  3. #include <vector>
  4. #include <unordered_set>
  5.  
  6. using namespace std;
  7.  
  8. // computes a^b in O(log b)
  9. long long fast_pow(long long a, int p, long long MOD)
  10. {
  11.   if(p == 0) return 1;
  12.   long long half = fast_pow(a, p/2, MOD);
  13.   if(p%2 == 0) return half * half % MOD;
  14.   return a * half % MOD * half % MOD;
  15. }
  16.  
  17. // compute inverse_mod(n, MOD) in O(log MOD)
  18. long long inverse_mod(long long n, long long MOD){ return  fast_pow(n, MOD - 2, MOD); }
  19.  
  20. struct rolling_hash
  21. {
  22.     long long p;
  23.     long long M;
  24.     vector<long long> P;
  25.     vector<long long> Pinv;
  26.     vector<long long> h;
  27.  
  28.     // returns a vector of size n where P[i] = p^a
  29.     vector<long long> prepare_P(long long p, long long M, int n)
  30.     {
  31.         P.assign(n, 0);
  32.         P[0] = 1;
  33.         for(int i=1;i<n;i++) P[i] = P[i-1] * p % M;
  34.         return P;
  35.     }
  36.  
  37.     // returns a vector of size n where Pinv[i] = inverse_mod(p^a, M)
  38.     vector<long long> prepare_Pinv(long long p, long long M, int n)
  39.     {
  40.         Pinv.assign(n+1, 0);
  41.         for(int a=0;a<n;a++) Pinv[a] = inverse_mod(fast_pow(p, a, M), M);
  42.         return Pinv;
  43.     }
  44.  
  45.     // instatiates a rolling hash by computing P, Pinv, and the total hash
  46.     rolling_hash(long long p, long long M, string s)
  47.     {
  48.         this->p = p;
  49.         this->M = M;
  50.         prepare_P(p, M, s.size());
  51.         prepare_Pinv(p, M, s.size());
  52.  
  53.         h.assign(s.size(), 0);
  54.         for(int i=0;i<s.size();i++)
  55.         {
  56.             if(i!=0) h[i] = h[i-1];
  57.             h[i] = (h[i] + (s[i] * P[i]) % M) % M;
  58.         }
  59.     }
  60.  
  61.     // returns hash(s[a...b]) in O(1)
  62.     long long hash_fast(int L, int R)
  63.     {
  64.         if(L==0) return h[R];
  65.         int ans = 0;
  66.         ans = ((h[R] - h[L-1]) % M + M) % M;
  67.         ans = ans * Pinv[L] % M;
  68.         return ans;
  69.     }
  70. };
  71.  
  72. // use this for unordered_set if you need more than 1 hash
  73. struct pair_hash
  74. {
  75.     template <class T1, class T2>
  76.     size_t operator () (pair<T1, T2> const& pair) const
  77.     {
  78.         size_t h1 = hash<T1>()(pair.first);
  79.         size_t h2 = hash<T2>()(pair.second);
  80.  
  81.         return h1 ^ h2;
  82.     }
  83. };
  84.  
  85. // computes the hash of a string in O(n)
  86. long long compute_hash(string& s) {
  87.     int p = 31;
  88.     int m = 1e9 + 9;
  89.     long long hash_value = 0;
  90.     long long p_pow = 1;
  91.     for (char c : s) {
  92.         hash_value = (hash_value + c * p_pow) % m;
  93.         p_pow = (p_pow * p) % m;
  94.     }
  95.     return hash_value;
  96. }
  97.  
  98. int main()
  99. {
  100.     string s = "supercallifragilisticexpialidocious";
  101.     int n = s.size();
  102.     rolling_hash rh1(31, 1e9+9, s);
  103.  
  104.     int tests_passed = 0;
  105.     int tests = 0;
  106.  
  107.     for (int i = 0; i < n; i++)
  108.     {
  109.         for (int j = i; j < n; j++)
  110.         {
  111.             string sub = s.substr(i, j - i + 1);
  112.             int fast_hash = rh1.hash_fast(i, j);
  113.             int slow_hash = compute_hash(sub);
  114.             cout << "Hashing substring: " << sub << '\n';
  115.             cout << "Slow substring hash O(n) found: " << slow_hash << '\n';
  116.             cout << "Fast substring hash O(1) found: " << fast_hash << '\n';
  117.             if (slow_hash == fast_hash)
  118.             {
  119.                 tests_passed++;
  120.                 cout << "passed\n\n";
  121.             }
  122.             else
  123.                 cout << "failed\n\n";
  124.             tests++;
  125.         }
  126.     }
  127.  
  128.     cout << tests_passed << " / " << tests << " tests passed!\n";
  129.  
  130. }
Advertisement
Add Comment
Please, Sign In to add comment