[CF] F. Isomorphic Strings - Educational Codeforces Round 44 (Rated for Div. 2)

https://codeforces.com/contest/985/problem/F

题目大意

给出字符串 $n \ (n \le 2 \times 10^5)$ 长的字符串 $S$,和 $m \ *(m \le 2 \times 10^5)$ 个询问。

定义串 $S$ 和 $T$ 是 isomorphic 的,当且仅当,$S$ 和 $T$ 的字符集之间能建立一一映射,使得 $S$ 和 $T$ 可以通过这组一一映射互相转化。

每组询问给出 $i, j, len$,问对于两个 $S$ 的子串 $S[i, i + len - 1]$ 与 $S[j, j + len - 1]$ 是不是 isomorphic 的。

简要题解

观察

  1. 注意到对于 isomorphic 的一对串,我们其实并不关注具体的字符是什么,我们关注的其实是每种字母的下标的集合。如果两个串对应的“每组字母下标集合”的集合满足一一映射,则其实它们是 isomorphic 的。
  2. 实际上我们只要关注 $S[i, i + len - 1]$ 中每个字母第一次出现的位置,看其对应的字符是什么,我们就知道,如果成立,那么字符的一一对应关系是怎样的。

有了以上的观察,我们可以处理 $26$ 个 $01$ 串,每个串只关注某个字母 $c$(原串如果是 $c$ 字母则新串为 $1$ 其他字母时为 $0$)。这样对于 $S[i, i + len - 1]$ 与 $S[j, j + len - 1]$ 由观察 2 可以知道每一组映射,我们检查对应的 $01$ 串是否匹配即可。

后缀数组因为多个 $log$ 所以被卡了。。。ST 表也不能复杂度更低,且会爆空间。

void solve() {
  int n, q; cin >> n >> q;
  string s; cin >> s;

  vector<vector<array<int, 3>>> qs(n);
  int x, y, len;
  for (int i = 0; i < q; i++) {
    cin >> x >> y >> len;
    x--; y--;
    qs[x].push_back({y, len, i});
  }

  vector<int> ss(n * 26);
  for (int i = 0; i < 26; i++) {
    for (int j = 0; j < n; j++) {
      if (s[j] - 'a' == i) ss[i * n + j] = 1;
    }
  }

  SA sa(ss);

  SegT segt(sa.lc);

  auto lcp = [&](int i, int j) -> int {
    // cerr << i << ' ' << j << ' ' << 26 * n - i << endl;
    if (i == j) return 26 * n - i;
    int a = sa.rk[i], b = sa.rk[j];
    if (a > b) swap(a, b);
    b--;
    return segt.query(a, b);
  };

  vector<int> ans(q, 1);
  vector<int> nxt(26, n);
  for (int i = n - 1; i >= 0; i--) {
    nxt[s[i] - 'a'] = i;
    for (auto [j, len, qid] : qs[i]) {
      for (int c = 0; c < 26; c++) {
        if (nxt[c] > i + len - 1) {
          continue;
        }

        int cj = s[j + (nxt[c] - i)] - 'a';

        if (lcp(c * n + i, cj * n + j) < len) {
          ans[qid] = 0;
          break;
        }
      }
    }
  }

  for (int i : ans) {
    cout << (i ? "YES" : "NO") << '\n';
  }
}

最后只好写双哈。

复杂度

$T$:$O((n + q)\Sigma)$

$S$:$O((n + q)\Sigma)$

代码实现

#include <bits/stdc++.h>
using namespace std;

int io_=[](){ ios::sync_with_stdio(false); cin.tie(nullptr); return 0; }();

using LL = long long;
using ULL = unsigned long long;
using LD = long double;
using PII = pair<int, int>;
using VI = vector<int>;
using MII = map<int, int>;

template<typename T> void cmin(T &x,const T &y) { if(y<x) x=y; }
template<typename T> void cmax(T &x,const T &y) { if(x<y) x=y; }
template<typename T> bool ckmin(T &x,const T &y) { 
    return y<x ? (x=y, true) : false; }
template<typename T> bool ckmax(T &x,const T &y) { 
    return x<y ? (x=y, true) : false; }
template<typename T> void cmin(T &x,T &y,const T &z) {// x<=y<=z 
    if(z<x) { y=x; x=z; } else if(z<y) y=z; }
template<typename T> void cmax(T &x,T &y,const T &z) {// x>=y>=z
    if(x<z) { y=x; x=z; } else if(y<z) y=z; }

// mt19937 rnd(chrono::system_clock::now().time_since_epoch().count());
// mt19937_64 rnd_64(chrono::system_clock::now().time_since_epoch().count());

/*
---------1---------2---------3---------4---------5---------6---------7---------
1234567890123456789012345678901234567890123456789012345678901234567890123456789
*/

const int D = 2;
struct Hash {
  using ULL = unsigned long long;
  static constexpr int MODS_LEN = 10;
  static array<ULL, MODS_LEN> MODS;
  static void genMod(int st = 1e9) {
    int p = 0;
    for (ULL i = st; p < MODS_LEN; i++) {
      int f = 1;
      for (ULL j = 2; j * j <= i; j++) {
        if (i % j == 0) { f = 0; break; }
      }
      if (f) MODS[p++] = i;
    }

    // for (auto i : MODS) cerr << i << ", ";
    // cerr << endl;
  }
  
  static mt19937_64 mt;
  static ULL rndi(ULL l, ULL r) { return mt() % (r - l + 1) + l; }

  static array<ULL, D> md;
  static vector<array<ULL, D>> pw, ipw;
  static ULL qp(ULL x, ULL n, ULL m) {
    ULL ans = 1;
    while (n) {
      if (n & 1) ans = ans * x % m;
      x = x * x % m;
      n >>= 1;
    }
    return ans;
  }
  static void init(int n) {
    vector<int> vis(MODS_LEN);
    for (int i = 0; i < D; i++) {
      int j = rndi(1, MODS_LEN - D);
      for (int k = 0; k < MODS_LEN; k++) {
        if (vis[k]) continue;
        if (!--j) {
          vis[k] = 1;
          md[i] = MODS[k];
          break;
        }
      }
    }

    pw.resize(n + 1);
    ipw.resize(n + 1);
    for (int i = 0; i < D; i++) {
      ULL b = rndi(500, 1000) * 2 + 1, ib = qp(b, md[i] - 2, md[i]);

      pw[0][i] = ipw[0][i] = 1;
      for (int j = 1; j <= n; j++) {
        pw[j][i] = pw[j - 1][i] * b % md[i];
        ipw[j][i] = ipw[j - 1][i] * ib % md[i];
      }
    }
  }

  int n;
  vector<array<ULL, D>> h;

  Hash(vector<char> s, bool need_init = true) : n(s.size()), h(n + 1) {
    static_assert(D <= MODS_LEN);
    if (need_init) init(n);

    for (int i = 0; i < n; i++) {
      for (int j = 0; j < D; j++) {
        h[i + 1][j] = (h[i][j] + pw[i][j] * s[i]) % md[j];
      }
    }
  }

  array<ULL, D> query(int l, int r) {
    array<ULL, D> ans;
    for (int i = 0; i < D; i++) {
      ans[i] = ipw[l][i] * (h[r + 1][i] - h[l][i] + md[i]) % md[i];
    }
    return ans;
  }
};
array<Hash::ULL, Hash::MODS_LEN> Hash::MODS = {1000000007, 1000000009, 
    1000000021, 1000000033, 1000000087, 1000000093, 1000000097, 1000000103, 
    1000000123, 1000000181,};
#ifdef OTTFF
mt19937_64 Hash::mt(42);
#else
mt19937_64 Hash::mt(chrono::high_resolution_clock::now().time_since_epoch().count());
#endif
array<Hash::ULL, D> Hash::md;
vector<array<Hash::ULL, D>> Hash::pw;
vector<array<Hash::ULL, D>> Hash::ipw;


void solve() {
  int n, q; cin >> n >> q;
  string s; cin >> s;

  vector<vector<array<int, 3>>> qs(n);
  int x, y, len;
  for (int i = 0; i < q; i++) {
    cin >> x >> y >> len;
    x--; y--;
    qs[x].push_back({y, len, i});
  }

  vector<char> ss(n * 26);
  for (int i = 0; i < 26; i++) {
    for (int j = 0; j < n; j++) {
      if (s[j] - 'a' == i) ss[i * n + j] = 1;
    }
  }

  Hash hsh(ss);

  auto check = [&](int ci, int i, int cj, int j, int len) {
    int li = ci * n + i, ri = li + len - 1;
    int lj = cj * n + j, rj = lj + len - 1;
    return hsh.query(li, ri) == hsh.query(lj, rj);
  };

  vector<int> ans(q, 1);
  vector<int> nxt(26, n);
  for (int i = n - 1; i >= 0; i--) {
    nxt[s[i] - 'a'] = i;
    for (auto [j, len, qid] : qs[i]) {
      for (int c = 0; c < 26; c++) {
        if (nxt[c] > i + len - 1) {
          continue;
        }

        int cj = s[j + (nxt[c] - i)] - 'a';

        if (!check(c, i, cj, j, len)) {
          ans[qid] = 0;
          break;
        }
      }
    }
  }

  for (int i : ans) {
    cout << (i ? "YES" : "NO") << '\n';
  }
}

int main() {
  // Hash::genMod();
  int t = 1; 
  // cin >> t;
  while (t--) {
    solve();
  }
  return 0;
}
Prev: [CF] D. Turtle Tenacity: Continual Mods - Codeforces Round 929 (Div. 3)
Next: [CF] E. Triangle Tree - Codeforces Round 1000 (Div. 2)