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(); }
未经授权,禁止转载。