UVA11107 Life Forms题解

题目传送门

题意

多组数据,每组给定一个 \(n\) 表示字符串个数,接下来 \(n\) 个字符串,求出现次数大于 \([\frac{n}{2}]\) 的所有串( \([x]\) 表示 \(x\) 向下取整),并按照字典序从小到大输出,每个字符串一行,注意两组数据间要有一个空行, \(n=0\) 时结束。( \(n \leq 100\) ,每个字符串的长度 \(|S| \leq 1000\)

题解

这里给出一种广义 SAM 做法,每次把所有串插入到 trie 中,trie 里的每个节点开一个 set,set 里存每个节点都在哪些串中出现。广义 SAM 的每个节点也要开一个 set,存当前 endpos 等价类在哪些串出现。构建广义 SAM 的时候,每个前缀的 set 直接从 trie 中对应前缀复制过来,之后 dfs 扫描一遍 parent tree,把儿子的 set 里的元素全部加入到当前节点,这样就求出了每个 endops 等价类出现在哪些串中,边 dfs 边处理出 \(size>[\frac{n}{2}]\) 的串中最大长度,之后走字符边按照字典序 dfs 到所有长度为最大长度且 \(size>[\frac{n}{2}]\) 的串,走到一个节点就把字符存到数组里,到达终点输出答案即可。

代码

// Problem: UVA11107 Life Forms
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/UVA11107
// Memory Limit: 0 MB
// Time Limit: 6666 ms
//
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define ll long long
using namespace std;
namespace Std {
int n;
char s[110][1010];
class Suffix_Automaton {
 private:
 public:
  int son[200100][26], tot = 1, len[200100], fa[200100];
  char ed[200100], ts[200100];
  int to[400100], nxt[400100], cnt = 0, head[200100], ans = 0;
  set<int> cset[200100];
  void clear(void);
  void insert(int x, int y, int z);
  void add(int x, int y);
  void build(void);
  void dfs(int x);
  void get(int x, int y);
} suffix_automaton;
class Trie {
 private:
 public:
  int son[100010][26], tot = 1, pos[100010], fa[100010];
  set<int> cset[100010];
  void insert(char *s, int x);
  void clear(void);
  void bfs(void);
} trie;
void Trie::insert(char *s, int x) {
  int i = 1, j = 0, len = strlen(s);
  while (j < len) {
    int opt = s[j] - 'a';
    if (!son[i][opt]) {
      son[i][opt] = ++tot;
      fa[tot] = i;
    }
    cset[son[i][opt]].insert(x);
    i = son[i][opt], ++j;
  }
}
void Trie::clear(void) {
  for (int i = 1; i <= tot; ++i) {
    memset(son[i], 0, sizeof(son[i]));
    cset[i].clear();
  }
  tot = 1;
  pos[1] = 1;
}
void Trie::bfs(void) {
  queue<pair<int, int> > q;
  for (int i = 0; i < 26; ++i) {
    if (son[1][i]) q.push(make_pair(son[1][i], i));
  }
  while (!q.empty()) {
    int x = q.front().first, y = q.front().second;
    q.pop();
    suffix_automaton.insert(x, fa[x], y);
    for (int i = 0; i < 26; ++i) {
      if (son[x][i]) q.push(make_pair(son[x][i], i));
    }
  }
}
void Suffix_Automaton::clear(void) {
  for (int i = 1; i <= tot; ++i) {
    memset(son[i], 0, sizeof(son[i]));
    head[i] = 0;
    cset[i].clear();
  }
  tot = 1, cnt = 0;
  ans = 0;
}
void Suffix_Automaton::insert(int x, int y, int z) {
  int np = trie.pos[x] = ++tot;
  int p = trie.pos[y];
  len[np] = len[p] + 1;
  ts[np] = z + 'a';
  cset[np] = trie.cset[x];
  while (p && (!son[p][z])) {
    son[p][z] = np;
    p = fa[p];
  }
  if (!p)
    fa[np] = 1;
  else {
    int q = son[p][z];
    if (len[q] == len[p] + 1)
      fa[np] = q;
    else {
      int nq = ++tot;
      memcpy(son[nq], son[q], sizeof(son[q]));
      len[nq] = len[p] + 1;
      fa[nq] = fa[q];
      fa[q] = fa[np] = nq;
      ts[nq] = ts[q];
      while (p && son[p][z] == q) {
        son[p][z] = nq;
        p = fa[p];
      }
    }
  }
}
void Suffix_Automaton::add(int x, int y) {
  to[++cnt] = y;
  nxt[cnt] = head[x];
  head[x] = cnt;
}
void Suffix_Automaton::build(void) {
  for (int i = 2; i <= tot; ++i) {
    add(fa[i], i);
  }
}
void Suffix_Automaton::dfs(int x) {
  for (int i = head[x]; i; i = nxt[i]) {
    dfs(to[i]);
    for (auto it = cset[to[i]].begin(); it != cset[to[i]].end(); it++) {
      if (cset[x].find(*it) == cset[x].end()) cset[x].insert(*it);
    }
  }
  if (cset[x].size() > (n >> 1)) ans = max(ans, len[x]);
}
void Suffix_Automaton::get(int x, int y) {
  if (cset[x].size() <= (n >> 1) && x != 1) return;
  if (y == ans) {
    for (int i = 1; i <= ans; ++i) putchar(ed[i]);
    puts("");
  } else {
    for (int i = 0; i < 26; ++i)
      if (son[x][i]) {
        ed[y + 1] = ts[son[x][i]];
        get(son[x][i], y + 1);
      }
  }
}
int main(void) {
  scanf("%d", &n);
  while (1) {
    trie.clear();
    suffix_automaton.clear();
    for (int i = 1; i <= n; ++i) {
      scanf("%s", s[i]);
      trie.insert(s[i], i);
    }
    trie.bfs();
    suffix_automaton.build();
    suffix_automaton.dfs(1);
    if (!suffix_automaton.ans)
      puts("?");
    else
      suffix_automaton.get(1, 0);
    scanf("%d", &n);
    if (!n) return 0;
    puts("");
  }
  return 0;
}
}  // namespace Std
int main(int argc, char *argv[]) { return Std::main(); }
posted @ 2022-05-09 20:56  Wilson_Inversion  阅读(9)  评论(0编辑  收藏  举报