Submission #436310

#TimeUsernameProblemLanguageResultExecution timeMemory
436310codebuster_10Sateliti (COCI20_satellti)C++17
110 / 110
755 ms41952 KiB
#include <bits/stdc++.h>

using namespace std;

#define f(i, a, b) for (int i = int(a); i < int(b); ++i)

#define vt vector
#define pb push_back

template < class A > void rd(vt < A > & v);
template < class T > void rd(T & x) {
  cin >> x;
}
template < class H, class...T > void rd(H & h, T & ...t) {
  rd(h);
  rd(t...);
}
template < class A > void rd(vt < A > & x) {
  for (auto & a: x) rd(a);
}

/*
 * description :- polynomial hashing of strings.
 * sources :- https://codeforces.com/contest/710/submission/102996468
 * verification :- https://codeforces.com/contest/154/submission/120344887
 */

#ifdef int
static_assert(false, "do not define int");
#endif

template <
  const unsigned & MOD >
    struct _m_uint {
      unsigned val;

      _m_uint(int64_t v = 0) {
        if (v < 0) v = v % MOD + MOD;
        if (v >= MOD) v %= MOD;
        val = unsigned(v);
      }

      _m_uint(uint64_t v) {
        if (v >= MOD) v %= MOD;
        val = unsigned(v);
      }

      _m_uint(int v): _m_uint(int64_t(v)) {}
      _m_uint(unsigned v): _m_uint(uint64_t(v)) {}

      explicit operator unsigned() const {
        return val;
      }
      explicit operator int64_t() const {
        return val;
      }
      explicit operator uint64_t() const {
        return val;
      }
      explicit operator double() const {
        return val;
      }
      explicit operator long double() const {
        return val;
      }

      _m_uint & operator += (const _m_uint & other) {
        val = val < MOD - other.val ? val + other.val : val - (MOD - other.val);
        return *this;
      }

      _m_uint & operator -= (const _m_uint & other) {
        val = val < other.val ? val + (MOD - other.val) : val - other.val;
        return *this;
      }

      static unsigned fast_mod(uint64_t x, unsigned m = MOD) {
        #if!defined(_WIN32) || defined(_WIN64)
        return unsigned(x % m);
        #endif
        // Optimized mod for Codeforces 32-bit machines.
        // x must be less than 2^32 * m for this to work, so that x / m fits in an unsigned 32-bit int.
        unsigned x_high = unsigned(x >> 32), x_low = unsigned(x);
        unsigned quot, rem;
        asm("divl %4\n": "=a"(quot), "=d"(rem): "d"(x_high), "a"(x_low), "r"(m));
        return rem;
      }

      _m_uint & operator *= (const _m_uint & other) {
        val = fast_mod(uint64_t(val) * other.val);
        return *this;
      }

      _m_uint & operator /= (const _m_uint & other) {
        return *this *= other.inv();
      }

      friend _m_uint operator + (const _m_uint & a,
        const _m_uint & b) {
        return _m_uint(a) += b;
      }
      friend _m_uint operator - (const _m_uint & a,
        const _m_uint & b) {
        return _m_uint(a) -= b;
      }
      friend _m_uint operator * (const _m_uint & a,
        const _m_uint & b) {
        return _m_uint(a) *= b;
      }
      friend _m_uint operator / (const _m_uint & a,
        const _m_uint & b) {
        return _m_uint(a) /= b;
      }

      _m_uint & operator++() {
        val = val == MOD - 1 ? 0 : val + 1;
        return *this;
      }

      _m_uint & operator--() {
        val = val == 0 ? MOD - 1 : val - 1;
        return *this;
      }

      _m_uint operator++(int) {
        _m_uint before = * this;
        ++ * this;
        return before;
      }
      _m_uint operator--(int) {
        _m_uint before = * this;
        -- * this;
        return before;
      }

      _m_uint operator - () const {
        return val == 0 ? 0 : MOD - val;
      }

      friend bool operator == (const _m_uint & a,
        const _m_uint & b) {
        return a.val == b.val;
      }
      friend bool operator != (const _m_uint & a,
        const _m_uint & b) {
        return a.val != b.val;
      }
      friend bool operator < (const _m_uint & a,
        const _m_uint & b) {
        return a.val < b.val;
      }
      friend bool operator > (const _m_uint & a,
        const _m_uint & b) {
        return a.val > b.val;
      }
      friend bool operator <= (const _m_uint & a,
        const _m_uint & b) {
        return a.val <= b.val;
      }
      friend bool operator >= (const _m_uint & a,
        const _m_uint & b) {
        return a.val >= b.val;
      }

      static
      const int SAVE_INV = int(1e6) + 5;
      static _m_uint save_inv[SAVE_INV];

      static void prepare_inv() {
        // Make sure MOD is prime, which is necessary for the inverse algorithm below.
        for (int64_t p = 2; p * p <= MOD; p += p % 2 + 1) {
          assert(MOD % p != 0);
        }

        save_inv[0] = 0;
        save_inv[1] = 1;

        for (int i = 2; i < SAVE_INV; i++) {
          save_inv[i] = save_inv[MOD % i] * (MOD - MOD / i);
        }
      }

      _m_uint inv() const {
        if (save_inv[1] == 0) prepare_inv();

        if (val < SAVE_INV) return save_inv[val];

        _m_uint product = 1;
        unsigned v = val;

        while (v >= SAVE_INV) {
          product *= MOD - MOD / v;
          v = MOD % v;
        }

        return product * save_inv[v];
      }

      _m_uint pow(int64_t p) const {
        if (p < 0) return inv().pow(-p);

        _m_uint a = * this, result = 1;

        while (p > 0) {
          if (p & 1) result *= a;
          p >>= 1;
          if (p > 0) a *= a;
        }

        return result;
      }

      friend ostream & operator << (ostream & os,
        const _m_uint & m) {
        return os << m.val;
      }
    };

template <
  const unsigned & MOD > _m_uint < MOD > _m_uint < MOD > ::save_inv[_m_uint < MOD > ::SAVE_INV];

auto random_address = [] {
  char * p = new char;
  delete p;
  return uint64_t(p);
};
mt19937_64 rng(chrono::steady_clock::now().time_since_epoch().count() * (random_address() | 1));

// P = 2^32 - 13337 is a safe prime: both P and (P - 1) / 2 are prime.
extern
const unsigned HASH_P = unsigned(-13337); // hash prime
using hash_int = _m_uint < HASH_P > ;
using hash_t = uint64_t;

const uint64_t HASH_P2 = uint64_t(HASH_P) * HASH_P; // square of hash prime
const int HASH_COUNT = 2;

// Avoid multiplication bases near 0 or P - 1.
uniform_int_distribution < unsigned > MULT_DIST(unsigned(0.1 * HASH_P), unsigned(0.9 * HASH_P));
const hash_int HASH_MULT[] = {
  MULT_DIST(rng),
  MULT_DIST(rng)
}; // our bases
const hash_int HASH_INV[] = {
  1 / HASH_MULT[0],
  1 / HASH_MULT[1]
};

vector < hash_int > hash_pow[] = {
  {
    1
  },
  {
    1
  }
};

const int INF = int(1e9) + 5;

template < typename T_string = string >
  struct string_hash {
    static
    const bool BUILD_REVERSE = false;

    static uint64_t hash(const T_string & str) {
      uint64_t result = 0;

      for (int h = 0; h < HASH_COUNT; h++) {
        uint64_t value = 1;
        for (const auto & x: str) {
          value = (uint64_t(HASH_MULT[h]) * value + x) % HASH_P;
        }

        result += value << (32 * h);
      }

      return result;
    }

    T_string str;
    vector < hash_int > _prefix[HASH_COUNT];
    vector < hash_int > _inv_prefix[HASH_COUNT];

    string_hash() {
      build({});
    }

    string_hash(const T_string & _str) {
      build(_str);
    }

    int length() const {
      return int(_prefix[0].size()) - 1;
    }

    template < typename T_char >
      void add_char(const T_char & c) {
        str.push_back(c);

        for (int h = 0; h < HASH_COUNT; h++) {
          _prefix[h].push_back(HASH_MULT[h] * _prefix[h].back() + c);

          if (hash_pow[h].size() < _prefix[h].size()) {
            hash_pow[h].push_back(HASH_MULT[h] * hash_pow[h].back());
          }

          if (BUILD_REVERSE) {
            _inv_prefix[h].push_back((_inv_prefix[h].back() + c) * HASH_INV[h]);
          }

        }
      }

    void pop_char() {
      str.pop_back();

      for (int h = 0; h < HASH_COUNT; h++) {
        _prefix[h].pop_back();

        if (BUILD_REVERSE) {
          _inv_prefix[h].pop_back();
        }

      }
    }

    void build(const T_string & _str) {
      str = {};
      str.reserve(_str.size());

      for (int h = 0; h < HASH_COUNT; h++) {
        hash_pow[h].reserve(_str.size() + 1);
        _prefix[h] = {
          0
        };
        _prefix[h].reserve(_str.size() + 1);

        if (BUILD_REVERSE) {
          _inv_prefix[h] = {
            0
          };
          _inv_prefix[h].reserve(_str.size() + 1);
        }
      }

      for (auto & c: _str) add_char(c);
    }

    uint64_t _single_hash(int h, int start, int end) const {
      // Convert everything to `uint64_t` for speed. Note: we add hash_pow[length] to fix strings that start with 0.
      uint64_t power = uint64_t(hash_pow[h][end - start]);
      return (power + uint64_t(_prefix[h][end]) + HASH_P2 - uint64_t(_prefix[h][start]) * power) % HASH_P;
    }

    uint64_t substring_hash(int start, int end) const {
      assert(0 <= start && start <= end && end <= length());
      return _single_hash(0, start, end) + (HASH_COUNT > 1 ? _single_hash(1, start, end) << 32 : 0);
    }

    uint64_t complete_hash() const {
      return substring_hash(0, length());
    }

    uint64_t _reverse_single_hash(int h, int start, int end) const {
      // Convert everything to `uint64_t` for speed. Note: we add hash_pow[length] to fix strings that start with 0.
      uint64_t power = uint64_t(hash_pow[h][end - start]);
      return (power + uint64_t(_inv_prefix[h][end]) * power + HASH_P - uint64_t(_inv_prefix[h][start])) % HASH_P;
    }

    uint64_t reverse_substring_hash(int start, int end) const {
      assert(0 <= start && start <= end && end <= length());
      return _reverse_single_hash(0, start, end) + (HASH_COUNT > 1 ? _reverse_single_hash(1, start, end) << 32 : 0);
    }

    uint64_t reverse_complete_hash() const {
      return reverse_substring_hash(0, length());
    }

    bool equal(int start1, int start2, int length) const {
      return substring_hash(start1, start1 + length) == substring_hash(start2, start2 + length);
    }

    bool is_palindrome(int start, int end) const {
      return substring_hash(start, end) == reverse_substring_hash(start, end);
    }

    int compare(int start1, int start2, int max_length = INF) const;
  };

uint64_t concat_hashes(uint64_t hash1, uint64_t hash2, int len2) {
  uint64_t hash1_low = hash1 & unsigned(-1);
  uint64_t hash2_low = hash2 & unsigned(-1);
  uint64_t power = uint64_t(hash_pow[0][len2]);
  uint64_t combined = (hash1_low * power + hash2_low + HASH_P - power) % HASH_P;

  if (HASH_COUNT > 1) {
    hash1 >>= 32;
    hash2 >>= 32;
    power = uint64_t(hash_pow[1][len2]);
    combined += (hash1 * power + hash2 + HASH_P - power) % HASH_P << 32;
  }

  return combined;
}

template < typename T_string >
  int first_mismatch(const string_hash < T_string > & hash1, int start1,
    const string_hash < T_string > & hash2, int start2, int max_length = INF) {
    max_length = min({
      max_length,
      hash1.length() - start1,
      hash2.length() - start2
    });

    static
    const int FIRST = 5;
    int first = min(max_length, FIRST);

    for (int i = 0; i < first; i++) {
      if (hash1.str[start1 + i] != hash2.str[start2 + i]) {
        return i;
      }
    }

    if (hash1.substring_hash(start1, start1 + max_length) == hash2.substring_hash(start2, start2 + max_length)) {
      return max_length;
    }

    static
    const int MANUAL = 15;
    int low = first, high = max_length - 1;

    while (high - low > MANUAL) {
      int mid = (low + high + 1) / 2;
      if (hash1.substring_hash(start1, start1 + mid) == hash2.substring_hash(start2, start2 + mid)) {
        low = mid;
      } else {
        high = mid - 1;
      }
    }

    for (int i = low; i < high; i++) {
      if (hash1.str[start1 + i] != hash2.str[start2 + i]) {
        return i;
      }
    }

    return high;
  }

template < typename T_string >
  int hash_compare(const string_hash < T_string > & hash1, int start1,
    const string_hash < T_string > & hash2, int start2, int max_length = INF) {
    int mismatch = first_mismatch(hash1, start1, hash2, start2, max_length);
    int length1 = min(hash1.length() - start1, max_length);
    int length2 = min(hash2.length() - start2, max_length);

    if (mismatch == min(length1, length2)) {
      return length1 == length2 ? 0 : (length1 < length2 ? -1 : +1);
    }

    if (hash1.str[start1 + mismatch] == hash2.str[start2 + mismatch]) {
      return 0;
    }

    return hash1.str[start1 + mismatch] < hash2.str[start2 + mismatch] ? -1 : +1;
  }

template < typename T_string >
  int string_hash < T_string > ::compare(int start1, int start2, int max_length) const {
    return hash_compare( * this, start1, * this, start2, max_length);
  }

template < class T > struct compress_1d_co_ordinates {

  vector < T > values;

  void add(T x) {
    values.push_back(x);
    return;
  }

  void gen() {
    sort(values.begin(), values.end());
    values.erase(unique(values.begin(), values.end()), values.end());
    return;
  }

  int get(T x) {
    int j = lower_bound(values.begin(), values.end(), x) - values.begin();
    assert(values[j] == x);
    return j;
  }

  void clear() {
    values.clear();
    return;
  }

};

signed main() {
  ios_base::sync_with_stdio(0);
  cin.tie(0);
  cout.tie(0);

  int n, m;
  rd(n, m);
  vector < string > s(n);
  rd(s);

  string_hash < string > string_h[2 * n];

  f(i, 0, n) {
    string_h[i].build(s[i] + s[i]);
    string_h[i + n].build(s[i] + s[i]);
  }

  vt < int > best(m);

  // rotote horizontally
  for (int i = 0; i < m; ++i) {
    vt < hash_t > _strings(2 * n);
    vt < int > strings;

    compress_1d_co_ordinates < hash_t > compress;

    for (int j = 0; j < n; ++j) {
      _strings[j] = _strings[n + j] = string_h[j].substring_hash(i, i + m);
      compress.add(_strings[j]);
    }

    compress.gen();

    for (auto & i: _strings) strings.pb(compress.get(i));

    compress.clear();

    string_hash < vector < int >> vector_h;
    vector_h.build(strings);

    int current_best = 0;
    for (int j = 1; j < n; ++j) {
      int k = first_mismatch(vector_h, current_best, vector_h, j, n);
      if (k == n) continue;

      int c = hash_compare(string_h[current_best + k], i, string_h[j + k], i, m);

      assert(c != 0);

      // if string 2 is small.
      if (c == 1) current_best = j;

    }

    best[i] = current_best;

  }

  int x = 0;
  for (int i = 1; i < m; ++i) {
    for (int j = 0; j < n; ++j) {
      if (string_h[best[x] + j].substring_hash(x, x + m) != string_h[best[i] + j].substring_hash(i, i + m)) {
        int c = hash_compare(string_h[best[x] + j], x, string_h[best[i] + j], i, m);

        assert(c != 0);

        // if string 2 is small

        if (c == 1) x = i;
        break;
      }
    }
  }

  f(i, 0, n) {
    f(j, 0, m) {
      cout << s[(i + best[x]) % n][(j + x) % m];
    }
    cout << "\n";
  }
}
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...