// 自动机 学习笔记 // AC 自动机的构建 voidbuild(){ for (int i = 0; i < 26; i++) { if (trie[0][i]) { fail[trie[0][i]] = 0; q.push(trie[0][i]); } } queue<int> q; while (!q.empty()) { int x = q.front(); q.pop(); for (int i = 0; i < 26; ++i) { int y = trie[x][i]; if (y) { fail[y] = trie[fail[x]][i]; q.push(y); } else trie[x][i] = trie[fail[x]][i]; } } }
int vis[Maxn]; voidsearch(char* s){ int n = strlen(s), p = 0; for (int i = 0; i < n; ++i) { p = trie[p][s[i] - 'a']; vis[p]++; } }
// 自动机 学习笔记 // AC自动机二次加强版 || AC自动机模板 // Code By CloudySky #include<bits/stdc++.h> // #define int long long namespace IO constint Maxn = 2e6 + 10; usingnamespace std;
int trie[Maxn][26], tot; int v[Maxn], id[Maxn]; voidinsert(char* s, int cur){ int p = 0, n = strlen(s); for (int k = 0; k < n; ++k) { int ch = s[k] - 'a'; if (!trie[p][ch]) trie[p][ch] = ++tot; p = trie[p][ch]; } v[p] = cur, id[cur] = p; }
int fail[Maxn];
voidbuild(){ queue<int> q; for (int i = 0; i < 26; i++) { if (trie[0][i]) { fail[trie[0][i]] = 0; q.push(trie[0][i]); } } while (!q.empty()) { int x = q.front(); q.pop(); for (int i = 0; i < 26; ++i) { int y = trie[x][i]; if (y) { fail[y] = trie[fail[x]][i]; q.push(y); } else trie[x][i] = trie[fail[x]][i]; } } }
int vis[Maxn];
voidsearch(char* s){ int n = strlen (s), p = 0; for (int i = 0; i < n; ++i) { p = trie[p][s[i] - 'a']; vis[p] ++; } }
structedge { int v, nxt; } e[Maxn << 1]; int hd[Maxn], cnt; voidadd(int u, int v){ e[++cnt] = (edge) {v, hd[u]}; hd[u] = cnt; }
voiddfs(int x){ for (int i = hd[x]; i; i = e[i].nxt) { int y = e[i].v; dfs (y); vis[x] += vis[y]; } }
signedmain(){ staticchar s[Maxn], s1[Maxn]; int n = read (); // clear for (int i = 0; i <= tot; ++i) fail[i] = v[i] = 0; memset (trie, 0, sizeof (trie)); memset (vis, 0, sizeof (vis)); tot = 0; // read for (int i = 1; i <= n; ++i) { scanf ("%s", s); insert (s, i); } // build build (); // query scanf ("%s", s1); search (s1); // count for (int i = 1; i <= tot; ++i) { add (fail[i], i); } dfs (0); // print for (int i = 1; i <= n; ++i) { print (vis[id[i]]); } return0; }
后缀自动机 SAM
后缀自动机是一个用途广泛的字符串处理算法。SAM 最重要的,也是它建立的初衷,是它维护了所有子串的信息。具体来说从初始节点 t0 开始的任意一条路径都与 s 的子串构成对应关系。但这并不能唯一确定一个自动机,因此它还具有一些其他的性质。
// 自动机 学习笔记 // 后缀自动机的线性构造 voidinsert(char c){ int p = lst, np = ++tot, ch = c - 'a'; lst = np, len[np] = len[p] + 1, siz[np] = 1; for (; p != -1 && !sam[p][ch]; p = fail[p]) sam[p][ch] = np; if (p == -1) return fail[np] = 0, void(); int q = sam[p][ch]; if (len[q] == len[p] + 1) return fail[np] = q, void(); int nq = ++tot; fail[nq] = fail[q], fail[q] = fail[np] = nq; memcpy(sam[nq], sam[q], sizeof(sam[nq])); len[nq] = len[p] + 1; for (; p != -1 && sam[p][ch] == q; p = fail[p]) sam[p][ch] = nq; }
voidSAM(char *s){ fail[0] = -1, lst = tot = 0; int n = strlen(s); for (int i = 0; i < n; ++i) insert(s[i]); staticint cnt[Maxn], id[Maxn]; for (int i = 0; i <= tot; ++i) ++cnt[len[i]]; for (int i = 0; i <= tot; ++i) cnt[i] += cnt[i - 1]; for (int i = 0; i <= tot; ++i) id[--cnt[len[i]]] = i; for (int i = tot; i; --i) siz[fail[id[i]]] += siz[id[i]]; }
int sam[Maxn][26], siz[Maxn], fail[Maxn], len[Maxn], lst, tot;
voidinsert(char c){ int p = lst, np = ++tot, ch = c - 'a'; lst = np, len[np] = len[p] + 1, siz[np] = 1; for (; p != -1 && !sam[p][ch]; p = fail[p]) sam[p][ch] = np; if (p == -1) return fail[np] = 0, void(); int q = sam[p][ch]; if (len[q] == len[p] + 1) return fail[np] = q, void(); int nq = ++tot; fail[nq] = fail[q], fail[q] = fail[np] = nq; memcpy(sam[nq], sam[q], sizeof(sam[nq])); len[nq] = len[p] + 1; for (; p != -1 && sam[p][ch] == q; p = fail[p]) sam[p][ch] = nq; }
char s[Maxn];
voidSAM(){ fail[0] = -1, lst = tot = 0; int n = strlen(s); for (int i = 0; i < n; ++i) insert(s[i]); staticint cnt[Maxn], id[Maxn]; for (int i = 0; i <= tot; ++i) ++cnt[len[i]]; for (int i = 0; i <= tot; ++i) cnt[i] += cnt[i - 1]; for (int i = 0; i <= tot; ++i) id[--cnt[len[i]]] = i; for (int i = tot; i; --i) siz[fail[id[i]]] += siz[id[i]]; }
signedmain(){ scanf("%s", s), SAM(); longlong ans = 0; for (int i = 1; i <= tot; ++i) ans = max(ans, siz[i] > 1 ? 1ll * siz[i] * len[i] : 0); print(ans); return0; }