SP220 PHRASES - Relevant Phrases of Annihilation题解

题目传送门

题意

\(t\) 组数据,每组 \(n\) 个字符串,求一个最长的字符串,使得它在这 \(n\) 个字符串中的每一个都至少出现过两次,并且存在两段是不重叠的。

做法

这里提供一种广义后缀自动机做法。

首先需要引入两个新的变量:

  • \(mx_{i\texttt{ }j}\):表示广义后缀自动机的第 \(i\) 个节点所表示的字符串在第 \(j\) 个原串中出现的最靠后的位置。
  • \(mn_{i\texttt{ }j}\):表示广义后缀自动机的第 \(i\) 个节点所表示的字符串在第 \(j\) 个原串中出现的最靠前的位置。

这样这个题就变成了板子题,先构建广义后缀自动机,构建的时候每一个新建节点的 \(mx\)\(mn\) 就是当前串出现的位置,之后再parent树上 \(dfs\),求出所有节点的 \(mx\)\(mn\),之后判断是否合法,对于节点 \(x\),合法条件是:

\[\forall i\in [1,n],mx_{x\texttt{ }i}-mn_{x\texttt{ }i}\ge len_x \]

这样就对于所有的合法串更新答案即可。

代码

// Problem: SP220 PHRASES - Relevant Phrases of Annihilation
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/SP220
// Memory Limit: 1500 MB
// Time Limit: 9000 ms
//
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
using namespace std;
namespace Std {
void read(int &x) {
  register int now = 0;
  char c = getchar();
  while (c < '0' || c > '9') c = getchar();
  while (c >= '0' && c <= '9') {
    now = (now << 1) + (now << 3) + (c & 15);
    c = getchar();
  }
  x = now;
}
int t, n, tot, lst, son[300010][26], len[300010], fa[300010], ans, pos[150010],
    mx[300010][11], mn[300010][11];
char s[15010];
vector<int> to[300010];
queue<pair<int, int> > q;
class trie {
 public:
  int tot = 1, son[150010][26], dep[150010], fa[150010];
  set<int> cset[150010];
  void insert(int x);
  void bfs(void);
} trie[10];
void ist(int x, set<int> *y, int z, int l, int r);
void dfs(int x);
void trie::insert(int x) {
  int now = 1;
  int len = strlen(s + 1);
  for (int i = 1; i <= len; ++i) {
    if (!son[now][s[i] - 'a']) {
      this->son[now][s[i] - 'a'] = ++this->tot;
      dep[this->tot] = dep[now] + 1;
      this->fa[this->tot] = now;
    }
    now = this->son[now][s[i] - 'a'];
    cset[now].insert(x);
  }
}
void trie::bfs(void) {
  pos[1] = 1;
  for (int i = 0; i < 26; ++i) {
    if (son[1][i]) q.push(make_pair(son[1][i], i));
  }
  while (!q.empty()) {
    int u = q.front().first, v = q.front().second;
    q.pop();
    ist(v, (cset + u), dep[u], u, this->fa[u]);
    for (int i = 0; i < 26; ++i) {
      if (this->son[u][i]) q.push(make_pair(this->son[u][i], i));
    }
  }
}
void ist(int x, set<int> *y, int z, int l, int r) {
  int p = pos[r];
  int np = pos[l] = ++tot;
  len[np] = len[p] + 1;
  if (tot == 20) {
    ++tot, --tot;
  }
  for (auto i = y->begin(); i != y->end(); i++) {
    mx[np][*i] = mn[np][*i] = z;
  }
  while (p && (!son[p][x])) {
    son[p][x] = np;
    p = fa[p];
  }
  if (!p)
    fa[np] = 1;
  else {
    int q = son[p][x];
    if (len[q] == len[p] + 1)
      fa[np] = q;
    else {
      int nq = ++tot;
      if (tot == 20) {
        ++tot, --tot;
      }
      memcpy(son[nq], son[q], sizeof(son[nq]));
      fa[nq] = fa[q];
      len[nq] = len[p] + 1;
      fa[np] = fa[q] = nq;
      while (p && son[p][x] == q) {
        son[p][x] = nq;
        p = fa[p];
      }
    }
  }
}
void dfs(int x) {
  for (auto i : to[x]) {
    dfs(i);
    for (int j = 1; j <= n; ++j) {
      mx[x][j] = max(mx[x][j], mx[i][j]);
      mn[x][j] = min(mn[x][j], mn[i][j]);
    }
  }
  if (len[x] < ans) return;
  for (int i = 1; i <= n; ++i) {
    if (mx[x][i] - mn[x][i] < len[x]) return;
  }
  ans = len[x];
}
int main() {
  read(t);
  memset(mx, 0, sizeof(mx));
  memset(mn, 0x3f, sizeof(mn));
  while (t--) {
    for (int i = 1; i <= tot; ++i) {
      memset(son[i], 0, sizeof(son[i]));
      memset(mx[i], 0, sizeof(mx[i]));
      memset(mn[i], 0x3f, sizeof(mn[i]));
      to[i].clear();
    }
    tot = 1;
    ans = 0;
    read(n);
    for (int i = 1; i <= n; ++i) {
      scanf("%s", s + 1);
      trie[t].insert(i);
    }
    trie[t].bfs();
    for (int i = 2; i <= tot; ++i) to[fa[i]].push_back(i);
    dfs(1);
    printf("%d\n", ans);
  }
  return 0;
}
}  // namespace Std
int main() { return Std::main(); }
posted @ 2022-05-10 21:58  Wilson_Inversion  阅读(14)  评论(1编辑  收藏  举报