ハッシュを計算する構造体

タイトルの通りです。
衝突しにくい構造体が作りたかった(衝突すると悲しいので)

コード

struct Hash
{
  static constexpr ll M1 = (1LL << 61) - 1;
  static constexpr ll M2 = (1LL << 31) - 1;
  ll val1, val2;

  constexpr Hash(ll val = 0) noexcept
      : val1(val), val2(val) {}

  constexpr Hash(ll val1, ll val2) noexcept
      : val1(val1), val2(val2 % M2)
  {
    if (val1 >= M1)
      val1 -= M1;
  }

  constexpr Hash operator-() const noexcept
  {
    return Hash(val1 ? M1 - val1 : 0, val2 ? M2 - val2 : 0);
  }
  constexpr Hash operator+=(const Hash &h) noexcept
  {
    val1 += h.val1;
    if (val1 > M1)
      val1 -= M1;
    val2 += h.val2;
    if (val2 > M2)
      val2 -= M2;
    return *this;
  }
  constexpr Hash operator-=(const Hash &h) noexcept
  {
    val1 -= h.val1;
    if (val1 < 0)
      val1 += M1;
    val2 -= h.val2;
    if (val2 < 0)
      val2 += M2;
    return *this;
  }
  Hash operator*=(const Hash &h) noexcept
  {
    __int128 t;
    t = val1;
    t *= h.val1;
    t = (t >> 61) + (t & M1);
    if (t >= M1)
      t -= M1;
    val1 = ll(t);
    val2 *= h.val2;
    val2 = (val2 >> 31) + (val2 & M2);
    if (val2 >= M2)
      val2 -= M2;
    return *this;
  }
  constexpr Hash operator+(const Hash &h) const noexcept
  {
    return Hash(*this) += h;
  }
  constexpr Hash operator-(const Hash &h) const noexcept
  {
    return Hash(*this) -= h;
  }
  Hash operator*(const Hash &h) const noexcept
  {
    return Hash(*this) *= h;
  }
  constexpr bool operator==(const Hash &h) const noexcept
  {
    return val1 == h.val1 and val2 == h.val2;
  }
  constexpr bool operator!=(const Hash &h) const noexcept
  {
    return !(*this == h);
  }
  constexpr bool operator<(const Hash &h) const noexcept
  {
    return val1 == h.val1 ? val2 < h.val2 : val1 < h.val1;
  }
  friend ostream &operator<<(ostream &os, const Hash &h) noexcept
  {
    return os << "(" << h.val1 << ", " << h.val2 << ")";
  }
};

mod2^61-1とmod2^31-1をペアで持ってます。比較演算子<はsetとかに入れる用です。
定数倍は速いはず。

あとローリングハッシュのライブラリも作ったので置いておきます。

class RollingHash
{
  Hash B;
  pair<Hash, Hash> _calc(string &s, int l, int r)
  {
    assert(l <= r);
    Hash res, p = 1;
    for (int i = r - 1; i >= l; i--)
    {
      res += p * ll(s[i]);
      if (i != l)
        p *= B;
    }
    return {res, p};
  }

public:
  RollingHash()
  {
    random_device seed;
    mt19937 engine(seed());
    B = Hash(ll(engine()) + 1000, ll((engine()) + 1000));
  }

  // [l, r) のハッシュを計算
  Hash calc(string &s, int l = -1, int r = -1)
  {
    if (l == -1)
      l = 0, r = s.length();
    return _calc(s, l, r).first;
  }

  // s の長さ m の部分文字列のハッシュ
  vector<Hash> Roll(string &s, int m)
  {
    vector<Hash> hash;
    int n = s.length();
    assert(n >= m);
    hash.resize(n - m + 1);
    auto [h, p] = _calc(s, 0, m);
    hash[0] = h;
    for (int i = m; i < n; i++)
    {
      h -= p * ll(s[i - m]);
      h *= B;
      h += ll(s[i]);
      hash[i - m + 1] = h;
    }
    return hash;
  }
};

機能が少ないのでもう少し整備したい

Verify