作为礼物的字符串

题目链接

1

首先这道题最暴力的想法就是给每个相同的字符在并查集上合并,最后暴力判断,但是这样复杂度是 \(O(n^2)\) 会得到一个 TLE

由于并查集具有可以合并的性质,因此可以使用神奇的 倍增

对于一个长度为 \(n\) 的回文串 \(s\) 来说,如果我们复制一份原字符串到末尾,那么对于一个回文子串 \(s(l,r)\),那么 \(s(l,r) = s(n + l, n + r)\),那么我们可以用一个并查集 fa[u][k] 表示 以 \(u\) 为起点,长度为 \(2 ^ k\) 的子串的根。
即如果 \(fa[i][k] == fa[j][k]\) 那么 \(s(i, i + 2^k - 1) == s(j, j + 2^k - 1)\)

然后对于最后的 \(fa[u][k] = pos\) 我们可以将长度为 \(2^k\) 的子串拆分成两个长度为 \(2^{k-1}\)的子串,然后重新进行 merge,如果最后判断长度为 \(2^0\) 的所有字符是否符合回文串的条件即可。

代码

点击查看代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;

constexpr int N = 2E5 + 10;

int fa[N][20];

int find(int x, int k) {
  if (fa[x][k] != x) fa[x][k] = find(fa[x][k], k);
  return fa[x][k];
}

void merge(int x, int y, int k) {
  x = find(x, k), y = find(y, k);
  if (x != y) fa[x][k] = y;
}

void solve() {
  int n, m; cin >> n >> m;
  int bit = __lg(n * 2);
  for (int i = 1; i <= n * 2; i ++ ) {
    for (int j = 0; j <= bit; j ++ ) {
      if (j == 0)
        fa[i][j] = i <= n ? i : 2 * n - i + 1;
      else 
        fa[i][j] = i;
    }
  }
  while (m -- ) {
    int l1, r1; cin >> l1 >> r1;
    int l2 = 2 * n - r1 + 1, r2 = 2 * n - l1 + 1;
    for (int k = bit; k >= 0; k -- ) {
      if (l1 + (1 << k) - 1 <= r1) {
        merge(l1, l2, k);
        l1 += 1 << k;
        l2 += 1 << k;
      }
    }
  }
  for (int k = bit; k >= 1; k -- ) {
    for (int i = 1; i + (1 << k) - 1 <= 2 * n; i ++ ) {
      int pos = find(i, k);
      merge(i, pos, k - 1);
      merge(i + (1 << k - 1), pos + (1 << k - 1), k - 1);
    }
  } 
  int cnt = 0;
  for (int i = 1; i <= n; i ++ ) if (find(i, 0) == find(n - i + 1, 0)) {
    ++ cnt;
  }
  if (cnt == n)
    cout << "YES\n";
  else 
    cout << "NO\n";
  
  cout << cnt << "\n";

}
int main() {
  cin.tie(nullptr)->sync_with_stdio(false);
  solve();
  return 0;
}

同类型

posted @ 2022-10-28 17:08  ccz9729  阅读(31)  评论(1编辑  收藏  举报