Problem p02589 (C++) — buggy submission, verdict: Time Limit Exceeded

#include <bits/stdc++.h>
using namespace std;
template <class T, class U>
ostream &operator<<(ostream &os, const pair<T, U> &p) {
  os << "(" << p.first << "," << p.second << ")";
  return os;
}
#ifdef __LOCAL
#define debug(x) cerr << __LINE__ << ": " << #x << " = " << x << endl
#define debugArray(x, n)                                                       \\
  cerr << __LINE__ << ": " << #x << " = {";                                    \\
  for (long long hoge = 0; (hoge) < (n); ++(hoge))                             \\
    cerr << ((hoge) ? "," : "") << x[hoge];                                    \\
  cerr << "}" << endl
#else
#define debug(x) (void(0))
#define debugArray(x, n) (void(0))
#endif

struct RollingHash {
private:
  static constexpr uint64_t mod = (1ull << 61ull) - 1;
  vector<uint64_t> hash, pw;
  uint64_t base;

private:
  static inline uint64_t add(uint64_t a, uint64_t b) {
    if ((a += b) >= mod)
      a -= mod;
    return a;
  }
  static inline uint64_t mul(uint64_t a, uint64_t b) {
    __uint128_t c = (__uint128_t)a * b;
    return add(c >> 61, c & mod);
  }

public:
  static inline uint64_t generate_base() {
    mt19937_64 mt(chrono::steady_clock::now().time_since_epoch().count());
    uniform_int_distribution<uint64_t> rand(1, RollingHash::mod - 1);
    return rand(mt);
  }

  RollingHash() = default;
  RollingHash(const string &s, uint64_t base)
      : RollingHash(vector<char>(s.begin(), s.end()), base) {}
  template <typename T>
  RollingHash(const vector<T> &s, uint64_t base) : base(base) {
    hash.assign(s.size() + 1, 0);
    pw.assign(s.size() + 1, 0);
    pw[0] = 1;
    for (size_t i = 0; i < s.size(); i++) {
      pw[i + 1] = mul(pw[i], base);
      hash[i + 1] = add(mul(hash[i], base), s[i]);
    }
  }
  // S[l, r)
  uint64_t get_hash(int l, int r) const {
    return add(hash[r], mod - mul(hash[l], pw[r - l]));
  }
  uint64_t combine_hash(uint64_t hash1, uint64_t hash2, int hash2len) {
    return add(mul(hash1, pw[hash2len]), hash2);
  }
};

struct RollingHash_MultiBase {
  vector<RollingHash> rhs;
  vector<uint64_t> bases;
  RollingHash_MultiBase() = default;
  RollingHash_MultiBase(const string &s, vector<uint64_t> bs)
      : RollingHash_MultiBase(vector<char>(s.begin(), s.end()), bs) {}
  template <typename T>
  RollingHash_MultiBase(const vector<T> &s, vector<uint64_t> bs) : bases(bs) {
    for (auto base : bases)
      rhs.emplace_back(RollingHash(s, base));
  }
  vector<uint64_t> get_hash(int l, int r) const {
    vector<uint64_t> ret(rhs.size());
    for (size_t i = 0; i < rhs.size(); i++)
      ret[i] = rhs[i].get_hash(l, r);
    return ret;
  }
  bool equal(int l1, int r1, int l2, int r2) const {
    return equal(*this, *this, l1, r1, l2, r2);
  }
  int lcp(int l1, int r1, int l2, int r2) const {
    return lcp(*this, *this, l1, r1, l2, r2);
  }
  static bool equal(const RollingHash_MultiBase &a,
                    const RollingHash_MultiBase &b, int l1, int r1, int l2,
                    int r2) {
    assert(a.bases.size() == b.bases.size());
    for (size_t i = 0; i < a.bases.size(); i++)
      if (a.rhs[i].get_hash(l1, r1) != b.rhs[i].get_hash(l2, r2))
        return false;
    return true;
  }
  static int lcp(const RollingHash_MultiBase &a, const RollingHash_MultiBase &b,
                 int l1, int r1, int l2, int r2) {
    int len = min(r1 - l1, r2 - l2);
    int low = 0, high = len + 1;
    while (high - low > 1) {
      int mid = (low + high) / 2;
      if (equal(a, b, l1, l1 + mid, l2, l2 + mid))
        low = mid;
      else
        high = mid;
    }
    return low;
  }
};

double tick() {
  static clock_t oldtick;
  clock_t newtick = clock();
  double diff = 1.0 * (newtick - oldtick) / CLOCKS_PER_SEC;
  oldtick = newtick;
  return diff;
}

signed main() {
  cin.tie(0);
  ios::sync_with_stdio(0);
  tick();
  auto base1 = RollingHash::generate_base();
  int N;
  cin >> N;
  string S[N];
  for (int i = 0; i < N; i++)
    cin >> S[i];
  sort(S, S + N, [](string s, string t) { return s.length() < t.length(); });
  RollingHash rh[N];
  for (int i = 0; i < N; i++)
    rh[i] = RollingHash(S[i], base1);
  // cerr << tick() << '\\n' << flush;
  unordered_map<uint64_t, vector<long long>> mp;
  long long ans = 0;
  for (int i = 0; i < N; i++) {
    int n = S[i].length();
    uint32_t bits = 0;
    for (int j = 0; j < n; j++) {
      bits |= 1 << (S[i][j] - 'a');
      auto itr = mp.find(rh[i].get_hash(j + 1, n));
      if (itr == mp.end())
        continue;
      for (int k = 0; k < 26; k++)
        if ((bits >> k) & 1)
          ans += itr->second[k];
    }
    auto hash = rh[i].get_hash(1, n);
    if (mp.find(hash) == mp.end())
      mp[hash].assign(26, 0);
    mp[hash][S[i][0] - 'a']++;
  }
  cout << ans << '\\n';
  // cerr << tick() << '\\n' << flush;
  return 0;
}


Error: TLE

Fixed submission:

#include <bits/stdc++.h>
using namespace std;
template <class T, class U>
ostream &operator<<(ostream &os, const pair<T, U> &p) {
  os << "(" << p.first << "," << p.second << ")";
  return os;
}
#ifdef __LOCAL
#define debug(x) cerr << __LINE__ << ": " << #x << " = " << x << endl
#define debugArray(x, n)                                                       \\
  cerr << __LINE__ << ": " << #x << " = {";                                    \\
  for (long long hoge = 0; (hoge) < (n); ++(hoge))                             \\
    cerr << ((hoge) ? "," : "") << x[hoge];                                    \\
  cerr << "}" << endl
#else
#define debug(x) (void(0))
#define debugArray(x, n) (void(0))
#endif

struct RollingHash {
private:
  static constexpr uint64_t mod = (1ull << 61ull) - 1;
  vector<uint64_t> hash, pw;
  uint64_t base;

private:
  static inline uint64_t add(uint64_t a, uint64_t b) {
    if ((a += b) >= mod)
      a -= mod;
    return a;
  }
  static inline uint64_t mul(uint64_t a, uint64_t b) {
    __uint128_t c = (__uint128_t)a * b;
    return add(c >> 61, c & mod);
  }

public:
  static inline uint64_t generate_base() {
    mt19937_64 mt(chrono::steady_clock::now().time_since_epoch().count());
    uniform_int_distribution<uint64_t> rand(1, RollingHash::mod - 1);
    return rand(mt);
  }

  RollingHash() = default;
  RollingHash(const string &s, uint64_t base)
      : RollingHash(vector<char>(s.begin(), s.end()), base) {}
  template <typename T>
  RollingHash(const vector<T> &s, uint64_t base) : base(base) {
    hash.assign(s.size() + 1, 0);
    pw.assign(s.size() + 1, 0);
    pw[0] = 1;
    for (size_t i = 0; i < s.size(); i++) {
      pw[i + 1] = mul(pw[i], base);
      hash[i + 1] = add(mul(hash[i], base), s[i]);
    }
  }
  // S[l, r)
  uint64_t get_hash(int l, int r) const {
    return add(hash[r], mod - mul(hash[l], pw[r - l]));
  }
  uint64_t combine_hash(uint64_t hash1, uint64_t hash2, int hash2len) {
    return add(mul(hash1, pw[hash2len]), hash2);
  }
};

struct RollingHash_MultiBase {
  vector<RollingHash> rhs;
  vector<uint64_t> bases;
  RollingHash_MultiBase() = default;
  RollingHash_MultiBase(const string &s, vector<uint64_t> bs)
      : RollingHash_MultiBase(vector<char>(s.begin(), s.end()), bs) {}
  template <typename T>
  RollingHash_MultiBase(const vector<T> &s, vector<uint64_t> bs) : bases(bs) {
    for (auto base : bases)
      rhs.emplace_back(RollingHash(s, base));
  }
  vector<uint64_t> get_hash(int l, int r) const {
    vector<uint64_t> ret(rhs.size());
    for (size_t i = 0; i < rhs.size(); i++)
      ret[i] = rhs[i].get_hash(l, r);
    return ret;
  }
  bool equal(int l1, int r1, int l2, int r2) const {
    return equal(*this, *this, l1, r1, l2, r2);
  }
  int lcp(int l1, int r1, int l2, int r2) const {
    return lcp(*this, *this, l1, r1, l2, r2);
  }
  static bool equal(const RollingHash_MultiBase &a,
                    const RollingHash_MultiBase &b, int l1, int r1, int l2,
                    int r2) {
    assert(a.bases.size() == b.bases.size());
    for (size_t i = 0; i < a.bases.size(); i++)
      if (a.rhs[i].get_hash(l1, r1) != b.rhs[i].get_hash(l2, r2))
        return false;
    return true;
  }
  static int lcp(const RollingHash_MultiBase &a, const RollingHash_MultiBase &b,
                 int l1, int r1, int l2, int r2) {
    int len = min(r1 - l1, r2 - l2);
    int low = 0, high = len + 1;
    while (high - low > 1) {
      int mid = (low + high) / 2;
      if (equal(a, b, l1, l1 + mid, l2, l2 + mid))
        low = mid;
      else
        high = mid;
    }
    return low;
  }
};

double tick() {
  static clock_t oldtick;
  clock_t newtick = clock();
  double diff = 1.0 * (newtick - oldtick) / CLOCKS_PER_SEC;
  oldtick = newtick;
  return diff;
}

template <class _Key, class _Tp, class _Hash, bool DOWNSIZE>
class UnorderedMapIterator;

template <class _Key, class _Tp, class _Hash = hash<_Key>,
          bool DOWNSIZE = false>
class UnorderedMap {
private:
  using iterator = UnorderedMapIterator<_Key, _Tp, _Hash, DOWNSIZE>;
  using value_type = _Tp;
  using data_type = pair<_Key, _Tp>;
  using aligned_pointer =
      typename aligned_storage<sizeof(value_type), alignof(value_type)>::type;
  friend UnorderedMapIterator<_Key, _Tp, _Hash, DOWNSIZE>;
  struct bucket {
    _Key _key;
    short int _dist;
    bool _last, _end;
    aligned_pointer _value_ptr;
    bucket() noexcept : _dist(-1), _last(false), _end(false) {}
    bucket &operator=(const bucket &another) noexcept {
      _key = another._key, _dist = another._dist, _last = another._last,
      _end = another._end;
      if (!another.empty()) {
        new (&_value_ptr) value_type(
            *reinterpret_cast<const value_type *>(&another._value_ptr));
      }
      return *this;
    }
    ~bucket() {
      if (!empty())
        _delete();
    }
    inline void clear() noexcept { _dist = -1; }
    inline void _delete() { _dist = -1, value_ptr()->~value_type(); }
    inline bool empty() const noexcept { return (_dist == -1); }
    inline value_type &value() noexcept {
      return *reinterpret_cast<value_type *>(&_value_ptr);
    }
    inline value_type *value_ptr() noexcept {
      return reinterpret_cast<value_type *>(&_value_ptr);
    }
    inline void new_value(value_type &&value) {
      new (&_value_ptr) value_type(move(value));
    }
  };
  inline static unsigned int ceilpow2(unsigned int u) noexcept {
    if (u == 0u)
      return 0u;
    --u, u |= u >> 1, u |= u >> 2, u |= u >> 4, u |= u >> 8;
    return (u | (u >> 16)) + 1u;
  }
  inline static bucket *increment(bucket *cur) noexcept {
    for (++cur; !cur->_end; ++cur) {
      if (!cur->empty())
        break;
    }
    return cur;
  }
  inline bucket *next_bucket(bucket *cur) const noexcept {
    return cur->_last ? _buckets : cur + 1;
  }
  inline unsigned int make_hash(const _Key &key) const noexcept {
    return _Hash()(key);
  }
  inline float load_rate() const noexcept {
    return (float)_data_count / _bucket_count;
  }
  bucket *insert(bucket *cur, _Key &&key, short int dist, value_type &&value) {
    bucket *ret = cur;
    bool flag = false;
    while (true) {
      if (cur->empty()) {
        cur->_key = move(key), cur->_dist = dist, cur->new_value(move(value));
        if (!flag)
          ret = cur, flag = true;
        break;
      } else if (dist > cur->_dist) {
        swap(key, cur->_key), swap(dist, cur->_dist), swap(value, cur->value());
        if (!flag)
          ret = cur, flag = true;
      }
      ++dist;
      cur = next_bucket(cur);
    }
    return ret;
  }
  template <class Key> bucket *_find(Key &&key, bool push = false) {
    unsigned int hash = make_hash(key);
    bucket *cur = _buckets + (hash & _mask);
    short int dist = 0;
    while (dist <= cur->_dist) {
      if (key == cur->_key)
        return cur;
      ++dist, cur = next_bucket(cur);
    }
    if (!push)
      return _buckets + _bucket_count;
    ++_data_count;
    if (rehash_check()) {
      cur = _buckets + (hash & _mask), dist = 0;
    }
    value_type new_value = value_type();
    _Key new_key = forward<Key>(key);
    return insert(cur, move(new_key), dist, move(new_value));
  }
  template <class Data> bucket *find_insert(Data &&data) {
    const _Key &key = data.first;
    unsigned int hash = make_hash(key);
    bucket *cur = _buckets + (hash & _mask);
    short int dist = 0;
    while (dist <= cur->_dist) {
      if (key == cur->_key)
        return cur;
      ++dist, cur = next_bucket(cur);
    }
    ++_data_count;
    if (rehash_check()) {
      cur = _buckets + (hash & _mask), dist = 0;
    }
    data_type new_data = forward<Data>(data);
    return insert(cur, move(new_data.first), dist, move(new_data.second));
  }
  template <typename... Args> bucket *emplace(Args &&...args) {
    return find_insert(data_type(forward<Args>(args)...));
  }
  bucket *backward_shift(bucket *cur, bool next_ret) {
    bucket *next = next_bucket(cur), *ret = cur;
    if (next->_dist < 1)
      return next_ret ? increment(cur) : cur;
    do {
      cur->_key = next->_key, cur->_dist = next->_dist - 1;
      cur->new_value(move(next->value()));
      cur = next, next = next_bucket(cur);
    } while (next->_dist >= 1);
    cur->clear();
    return ret;
  }
  bucket *erase_impl(bucket *cur, bool next_ret) {
    assert(static_cast<size_t>(cur - _buckets) != _bucket_count);
    cur->_delete();
    --_data_count;
    return backward_shift(cur, next_ret);
  }
  bucket *erase_itr(bucket *cur, bool next_ret = true) {
    const _Key key = cur->_key;
    return erase_impl(rehash_check() ? _find(key) : cur, next_ret);
  }
  size_t erase_key(const _Key &key) {
    rehash_check();
    bucket *cur = _find(key);
    if (static_cast<size_t>(cur - _buckets) == _bucket_count) {
      return 0;
    } else {
      erase_impl(_find(key), false);
      return 1;
    }
  }
  bool rehash_check() {
    if (_bucket_count == 0) {
      rehash(1u);
      return true;
    } else if (load_rate() >= MAX_LOAD_RATE) {
      rehash(_bucket_count * 2u);
      return true;
    } else if (DOWNSIZE) {
      if (load_rate() <= MIN_LOAD_RATE && _bucket_count >= DOWNSIZE_THRESHOLD) {
        rehash(_bucket_count / 2u);
        return true;
      }
    }
    return false;
  }
  void move_data(bucket *cur) {
    insert(_buckets + (make_hash(cur->_key) & _mask), move(cur->_key), 0,
           move(cur->value()));
  }
  void rehash(unsigned int new_bucket_count) {
    UnorderedMap new_unordered_map(new_bucket_count);
    new_unordered_map._data_count = _data_count;
    for (bucket *cur = _buckets; !cur->_end; ++cur) {
      if (!cur->empty()) {
        new_unordered_map.move_data(cur);
      }
    }
    swap(*this, new_unordered_map);
  }
  friend void swap(UnorderedMap &ump1, UnorderedMap &ump2) {
    swap(ump1._bucket_count, ump2._bucket_count);
    swap(ump1._mask, ump2._mask);
    swap(ump1._data_count, ump2._data_count);
    swap(ump1._buckets, ump2._buckets);
  }

private:
  unsigned int _bucket_count, _mask, _data_count;
  bucket *_buckets;

public:
  const float MAX_LOAD_RATE = 0.5f;
  const float MIN_LOAD_RATE = 0.1f;
  const unsigned int DOWNSIZE_THRESHOLD = 16u;
  UnorderedMap(unsigned int bucket_size = 0u)
      : _bucket_count(ceilpow2(bucket_size)), _mask(_bucket_count - 1),
        _data_count(0u), _buckets(new bucket[_bucket_count + 1]) {
    if (_bucket_count > 0)
      _buckets[_bucket_count - 1]._last = true;
    else
      _mask = 0;
    _buckets[_bucket_count]._end = true;
  }
  UnorderedMap(const UnorderedMap &another)
      : _bucket_count(another._bucket_count), _mask(another._mask),
        _data_count(another._data_count) {
    _buckets = new bucket[_bucket_count + 1u];
    for (unsigned int i = 0u; i <= _bucket_count; ++i) {
      _buckets[i] = another._buckets[i];
    }
  }
  UnorderedMap(UnorderedMap &&another)
      : _bucket_count(move(another._bucket_count)), _mask(move(another._mask)),
        _data_count(move(another._data_count)), _buckets(another._buckets) {
    another._buckets = nullptr;
  }
  UnorderedMap &operator=(const UnorderedMap &another) {
    delete[] _buckets;
    _bucket_count = another._bucket_count;
    _mask = another._mask;
    _data_count = another._data_count;
    _buckets = new bucket[_bucket_count + 1u];
    for (unsigned int i = 0u; i <= _bucket_count; ++i) {
      _buckets[i] = another._buckets[i];
    }
    return *this;
  }
  UnorderedMap &operator=(UnorderedMap &&another) {
    delete[] _buckets;
    _bucket_count = move(another._bucket_count);
    _mask = move(another._mask);
    _data_count = move(another._data_count);
    _buckets = another._buckets;
    another._buckets = nullptr;
    return *this;
  }
  void allocate(unsigned int element_size) {
    rehash(ceilpow2(ceil(element_size / MAX_LOAD_RATE) + 1));
  }
  ~UnorderedMap() { delete[] _buckets; }
  friend ostream &operator<<(ostream &os, UnorderedMap &ump) noexcept {
    for (auto val : ump)
      os << '{' << val.first << ',' << val.second << "} ";
    return os;
  }
  _Tp &operator[](const _Key &key) { return _find(key, true)->value(); }
  _Tp &operator[](_Key &&key) { return _find(move(key), true)->value(); }
  const _Tp &at(const _Key &key) {
    bucket *res = _find(key);
    if (res == _buckets + _bucket_count)
      __throw_out_of_range("Unordered_Map::at");
    return res->value();
  }
  void clear() {
    UnorderedMap new_unordered_map(0u);
    swap(*this, new_unordered_map);
  }
  size_t size() const noexcept { return _data_count; }
  size_t bucket_count() const noexcept { return _bucket_count; }
  bool empty() const noexcept { return (_data_count == 0); }
  iterator begin() noexcept {
    return (_buckets->empty() && _bucket_count > 0)
               ? iterator(increment(_buckets))
               : iterator(_buckets);
  }
  iterator end() noexcept { return iterator(_buckets + _bucket_count); }
  iterator find(const _Key &key) { return iterator(_find(key)); }
  iterator insert(const data_type &data) { return iterator(find_insert(data)); }
  iterator insert(data_type &&data) {
    return iterator(find_insert(move(data)));
  }
  template <typename... Args> iterator emplace(Args &&...args) {
    return iterator(_emplace(forward<Args>(args)...));
  }
  size_t erase(const _Key &key) { return erase_key(key); }
  iterator erase(const iterator &itr) {
    return iterator(erase_itr(itr.bucket_ptr));
  }
  void simple_erase(const _Key &key) { erase_key(key); }
  void simple_erase(const iterator &itr) { erase_itr(itr.bucket_ptr, false); }

  // DEBUG 用
  short int maximum_distance() const noexcept {
    short int ret = -1;
    for (bucket *cur = _buckets; !cur->_end; ++cur) {
      ret = max(ret, cur->_dist);
    }
    return ret;
  }
};

template <class _Key, class _Tp, class _Hash, bool DOWNSIZE>
class UnorderedMapIterator {
private:
  friend UnorderedMap<_Key, _Tp, _Hash, DOWNSIZE>;
  typename UnorderedMap<_Key, _Tp, _Hash, DOWNSIZE>::bucket *bucket_ptr;
  using iterator_category = forward_iterator_tag;
  using value_type = pair<const _Key, _Tp>;
  using difference_type = ptrdiff_t;
  using reference = pair<const _Key &, _Tp &>;

private:
  UnorderedMapIterator(typename UnorderedMap<_Key, _Tp, _Hash, DOWNSIZE>::bucket
                           *_bucket_ptr) noexcept
      : bucket_ptr(_bucket_ptr) {}

public:
  UnorderedMapIterator() noexcept : bucket_ptr() {}
  UnorderedMapIterator(const UnorderedMapIterator &itr) noexcept
      : bucket_ptr(itr.bucket_ptr) {}
  UnorderedMapIterator &operator=(const UnorderedMapIterator &itr) &noexcept {
    return bucket_ptr = itr.bucket_ptr, *this;
  }
  UnorderedMapIterator &operator=(const UnorderedMapIterator &&itr) &noexcept {
    return bucket_ptr = itr.bucket_ptr, *this;
  }
  reference operator*() const noexcept {
    return {bucket_ptr->_key, bucket_ptr->value()};
  }
  UnorderedMapIterator &operator++() noexcept {
    return bucket_ptr =
               UnorderedMap<_Key, _Tp, _Hash, DOWNSIZE>::increment(bucket_ptr),
           *this;
  }
  UnorderedMapIterator operator++(int) const noexcept {
    return UnorderedMapIterator(
        UnorderedMap<_Key, _Tp, _Hash, DOWNSIZE>::increment(this->bucket_ptr));
  }
  bool operator==(const UnorderedMapIterator &itr) const noexcept {
    return !(*this != itr);
  };
  bool operator!=(const UnorderedMapIterator &itr) const noexcept {
    return bucket_ptr != itr.bucket_ptr;
  }
};

signed main() {
  cin.tie(0);
  ios::sync_with_stdio(0);
  tick();
  auto base1 = RollingHash::generate_base();
  int N;
  cin >> N;
  string S[N];
  for (int i = 0; i < N; i++)
    cin >> S[i];
  RollingHash rh[N];
  for (int i = 0; i < N; i++)
    rh[i] = RollingHash(S[i], base1);
  // cerr << tick() << '\\n' << flush;
  UnorderedMap<uint64_t, long long> mp;
  for (int i = 0; i < N; i++) {
    int n = S[i].length();
    int cnt[26];
    fill(cnt, cnt + 26, 0);
    vector<char> cs;
    for (int j = 0; j <= n; j++) {
      if (j >= 2) {
        auto hash = rh[i].get_hash(j, n);
        for (auto c : cs) {
          mp[rh[i].combine_hash(c, hash, n - j)]++;
        }
      }
      if (j < n)
        if (cnt[S[i][j] - 'a']++ == 0)
          cs.push_back(S[i][j]);
    }
  }
  // cerr << tick() << '\\n' << flush;
  long long ans = 0;
  for (int i = 0; i < N; i++) {
    auto hash = rh[i].get_hash(0, S[i].length());
    // debugArray(hash, 2);
    ans += mp[hash];
  }
  cout << ans << '\\n';
  // cerr << tick() << '\\n' << flush;
  return 0;
}