后缀自动机学习笔记
来源:互联网 发布:淘宝 手环 编辑:程序博客网 时间:2024/06/03 21:21
诸神眷顾的幻想乡
给定一个叶节点不超过20的无根树,每个节点有一个字母。问树上路径形成的本质不同的字符串的个数。
广义后缀自动机裸题。从每个叶节点做bfs,记录父亲的状态从而插入建立后缀自动机。我们知道一个后缀自动机本质不同的子串个数为
#include <bits/stdc++.h>using namespace std;const int MAXN = 100001*20, S = 10;struct SAM { int chl[MAXN][S], fa[MAXN], maxl[MAXN]; int top, root, last; void init() { top = root = last = 1; memset(chl, 0, sizeof chl); memset(fa, 0, sizeof fa); memset(maxl, 0, sizeof maxl); } void push(int stat, int x) { int p = stat, np = ++top; maxl[np] = maxl[p] + 1; while (p && !chl[p][x]) chl[p][x] = np, p = fa[p]; if (!p) fa[np] = root; else { int q = chl[p][x]; if (maxl[q] == maxl[p] + 1) fa[np] = q; else { int nq = ++top; maxl[nq] = maxl[p] + 1; memcpy(chl[nq], chl[q], sizeof chl[q]); fa[nq] = fa[q], fa[q] = fa[np] = nq; while (p && chl[p][x] == q) chl[p][x] = nq, p = fa[p]; } } last = np; }}sam;queue<int> que;int stat[102400], rd[102400], col[102400];struct node { int to, next;} edge[202400];int head[102400], top = 0;void push(int i, int j){ rd[i]++, ++top, edge[top] = (node) {j, head[i]}, head[i] = top; }void bfs(int nd){ //printf("BFS : %d\n", nd); memset(stat, 0, sizeof stat); stat[nd] = 1; que.push(nd); while (!que.empty()) { int tp = que.front(); que.pop(); sam.push(stat[tp], col[tp]); //printf("%d -- %d--+%d-->%d\n", tp, stat[tp], col[tp], sam.last); for (int i = head[tp]; i; i = edge[i].next) if (!stat[edge[i].to]) stat[edge[i].to] = sam.last, que.push(edge[i].to); }}int n, c;void solve(){ sam.init(); scanf("%d%d", &n, &c); for (int i = 1; i <= n; i++) scanf("%d", &col[i]); for (int i = 1; i < n; i++) { int u, v; scanf("%d%d", &u, &v); push(u, v); push(v, u); } for (int i = 1; i <= n; i++) if (rd[i] == 1) bfs(i); long long ans = 0; for (int i = 2; i <= sam.top; i++) ans += sam.maxl[i] - sam.maxl[sam.fa[i]]; printf("%lld", ans);}int main(){ //freopen("zjoi15_substring.in", "r", stdin); //freopen("zjoi15_substring.out", "w", stdout); solve(); return 0;}
POI2000 公共串
给你
SAM解法:只要在匹配的时候记录每个节点对于第i个串匹配的最长距离,然后xjb取max和min就好了。
APIO2014 回文串
给定一个字符串
首先我们用manacher算法求出本质不同的回文串。由于manacher的复杂度为
首先我们预处理出
#include <bits/stdc++.h>using namespace std;const int MAXN = 300005*2, S = 26;int pos[MAXN]; // S[1..r]对应状态int fa[MAXN][21];char str[MAXN];int right_siz[MAXN];int stk[MAXN], top = 0, rd[MAXN];struct SAM { int chl[MAXN][S], fa[MAXN], maxl[MAXN]; int top, root, last; void clear() { top = root = last = 1; memset(chl, 0, sizeof chl), memset(fa, 0, sizeof fa), memset(maxl, 0, sizeof maxl); } SAM() { clear(); } void push(int x) { int p = last, np = ++top; maxl[np] = maxl[p] + 1, right_siz[np]++; while (p && !chl[p][x]) chl[p][x] = np, p = fa[p]; if (!p) fa[np] = root; else { int q = chl[p][x]; if (maxl[q] == maxl[p] + 1) fa[np] = q; else { int nq = ++top; maxl[nq] = maxl[p]+1; memcpy(chl[nq], chl[q], sizeof chl[q]); fa[nq] = fa[q], fa[q] = fa[np] = nq; while (p && chl[p][x] == q) chl[p][x] = nq, p = fa[p]; } } last = np; }} sam;void top_sort(){ for (int i = 1; i <= sam.top; i++) rd[sam.fa[i]]++; for (int i = 1; i <= sam.top; i++) if (rd[i] == 0) stk[++top] = i; while (top) { int t = stk[top--]; rd[sam.fa[t]]--, right_siz[sam.fa[t]] += right_siz[t]; if (rd[sam.fa[t]] == 0) stk[++top] = sam.fa[t]; }}void init(){ scanf("%s", str+1); for (char *p = str+1; *p != '\0'; ++p) sam.push(*p-'a'); int len = strlen(str+1); for (int i = 1, nd = sam.root; i <= len; i++) { nd = sam.chl[nd][str[i]-'a']; pos[i] = nd; } for (int i = 1; i <= sam.top; i++) fa[i][0] = sam.fa[i]; for (int j = 1; j <= 20; j++) for (int i = 1; i <= sam.top; i++) fa[i][j] = fa[fa[i][j-1]][j-1]; top_sort(); // Count right_siz} long long ans = 0;void query(int i, int j){ int nd = pos[j]; for (int k = 20; k >= 0; k--) if (sam.maxl[fa[nd][k]] >= j-i+1) nd = fa[nd][k]; ans = max(ans, (long long)(j-i+1)*right_siz[nd]);}int p[MAXN];void work(){ int len = strlen(str+1); int id = 0, mx = 0; // manacher str[0] = '$'; for (int i = 1; i <= len; i++) { if (mx > i) p[i] = min(p[id-(i-id)], mx-i); else p[i] = 1, query(i, i); while (str[i-p[i]] == str[i+p[i]]) query(i-p[i], i+p[i]), p[i]++; if (i+p[i] > mx) id = i, mx = i+p[i]; } id = mx = 0; for (int i = 1; i <= len; i++) { if (mx > i) p[i] = min(p[id-(i-id)], mx-i); else p[i] = 0; while (str[i-p[i]] == str[i+p[i]+1]) query(i-p[i], i+p[i]+1), p[i]++; if (i+p[i] > mx) id = i, mx = i+p[i]; } cout << ans << endl;}int main(){ init(); work(); return 0;}
HAOI2016找相同字符
给定两个串S1,S2,统计他们的公共子串总数。两个子串不同,当且仅当长度不同或出现位置不同。
SA解法:将S1和S2用一个’#’隔开,求出height数组,由于公共子串是后缀的前缀,因此答案就是所有前一半的后缀和后一半的后缀的lcp的和。用单调栈扫两遍记录答案即可。最优复杂度
SAM解法:这个做法比较鬼畜。先把第一个串建立后缀自动机,再把第二个串在上面跑。到达一个状态x时匹配长度为len对答案的贡献分为两部分:
- 当前位置的收获
(len−min(x)+1)×|Right(x)| - 由于匹配了当前位置,自然匹配了父亲节点,就有
∑i为x的祖先(max(i)−min(i)+1)×|Right(i)|
第一部分为
#include <bits/stdc++.h>using namespace std;const int MAXN = 200005*2, S = 26;int right_siz[MAXN];struct SAM { int chl[MAXN][S], fa[MAXN], maxl[MAXN]; int top, root, last; void clear() { top = root = last = 1; } SAM() { clear(); } void push(int x) { int p = last, np = ++top; maxl[np] = maxl[p] + 1; right_siz[np]++; while (p && !chl[p][x]) chl[p][x] = np, p = fa[p]; if (!p) fa[np] = root; else { int q = chl[p][x]; if (maxl[q] == maxl[p] + 1) fa[np] = q; else { int nq = ++top; maxl[nq] = maxl[p] + 1; memcpy(chl[nq], chl[q], sizeof chl[q]); fa[nq] = fa[q], fa[q] = fa[np] = nq; while (p && chl[p][x] == q) chl[p][x] = nq, p = fa[p]; } } last = np; }} sam;char s1[MAXN], s2[MAXN];int stk[MAXN], top = 0;int rd[MAXN];int topo[MAXN], tp_top = 0;int canc[MAXN];int vis[MAXN];void dfs(int nd, string str) { printf("Id = %d, pre = %d, dis = %d, right = %d, cacc = %d\n", nd, sam.fa[nd], sam.maxl[nd], right_siz[nd], canc[nd]); vis[nd] = 1; for (int i = 0; i < S; i++) if (sam.chl[nd][i]) printf("-+%c-> %d\n", i+'a', sam.chl[nd][i]); for (int i = 0; i < S; i++) if (sam.chl[nd][i] && !vis[sam.chl[nd][i]]) dfs(sam.chl[nd][i], str+char(i+'a'));}void top_sort(){ for (int i = 1; i <= sam.top; i++) rd[sam.fa[i]]++; for (int i = 1; i <= sam.top; i++) if (rd[i] == 0) stk[++top] = i; while (top) { int t = stk[top--]; topo[++tp_top] = t, rd[sam.fa[t]]--; if (rd[sam.fa[t]] == 0) stk[++top] = sam.fa[t]; } for (int i = 1; i <= tp_top; i++) right_siz[sam.fa[topo[i]]] += right_siz[topo[i]]; for (int i = tp_top; i >= 1; i--) if (topo[i] != sam.root && sam.fa[topo[i]] != sam.root) canc[topo[i]] = canc[sam.fa[topo[i]]] + (sam.maxl[sam.fa[topo[i]]]-sam.maxl[sam.fa[sam.fa[topo[i]]]])*right_siz[sam.fa[topo[i]]];}void work(){ scanf("%s%s", s1, s2); for (char *p = s1; *p != '\0'; p++) sam.push(*p-'a'); top_sort(); int nd = sam.root, len = 0; long long ans = 0; //int l = strlen(s2); s2[l] = '$', s2[l+1] = '\0'; for (char *p = s2; *p != '\0'; p++) { if (sam.chl[nd][*p-'a']) nd = sam.chl[nd][*p-'a'], len++; else { while (nd && !sam.chl[nd][*p-'a']) nd = sam.fa[nd]; if (!nd) nd = sam.root, len = 0; else len = sam.maxl[nd]+1, nd = sam.chl[nd][*p-'a']; } ans += canc[nd] + (len-sam.maxl[sam.fa[nd]])*right_siz[nd]; //cout << ans << endl; } cout << ans << endl;}int main(){ work(); return 0;}
2555: SubString
LCT维护Right数组大小…
神题。
#include <bits/stdc++.h>using namespace std;const int MAXN = 1600002, S = 26;struct LCT { int chl[MAXN][2], fa[MAXN], siz[MAXN], flag[MAXN], rev[MAXN], add[MAXN]; int stk[MAXN]; int root, top; void clear() { root = top = 0; } LCT() { clear(); } bool isrt(int nd) { return chl[fa[nd]][0] != nd && chl[fa[nd]][1] != nd; } void pdw(int nd) { int &lc = chl[nd][0], &rc = chl[nd][1]; if (lc) rev[lc] ^= rev[nd], add[lc] += add[nd]; if (rc) rev[rc] ^= rev[nd], add[rc] += add[nd]; if (rev[nd]) rev[nd] = 0, swap(lc, rc); if (add[nd]) siz[nd] += add[nd], add[nd] = 0; } void zig(int nd) { int p = fa[nd], g = fa[p]; int tp = chl[p][0] != nd, tg = chl[g][0] != p, son = chl[nd][tp^1]; if (!isrt(p)) chl[g][tg] = nd; chl[nd][tp^1] = p, chl[p][tp] = son; fa[nd] = g, fa[p] = nd, fa[son] = p; } void splay(int nd) { int top = 0; stk[++top] = nd; for (int x = nd; !isrt(x); x = fa[x]) stk[++top] = fa[x]; while (top) pdw(stk[top--]); while (!isrt(nd)) { int p = fa[nd], g = fa[p]; int tp = chl[p][0] != nd, tg = chl[g][0] != p; if (isrt(p)) { zig(nd); break; } else if (tp == tg) zig(p), zig(nd); else zig(nd), zig(nd); } } void dfs(int nd, int tab) { if (!nd) return; for (int i = 1; i <= tab; i++) putchar(' '); printf("nd = %d, flag = %d, siz = %d, lc = %d, rc = %d, fa = %d, rev = %d\n", nd, flag[nd], siz[nd], chl[nd][0], chl[nd][1], fa[nd], rev[nd]); dfs(chl[nd][0], tab+2); dfs(chl[nd][1], tab+2); } void access(int x) { for (int y = 0; x; x = fa[y = x]) splay(x), chl[x][1] = y; } void mkt(int x) { access(x), splay(x), rev[x] ^= 1; } void link(int x, int y) { mkt(x); splay(x); fa[x] = y; } void cut(int x, int y) { mkt(x), access(y), splay(y), fa[x] = chl[y][0] = 0;} void lct_link(int x, int y) // x->y { //printf("LINK : %d-->%d\n", x, y); link(x, y), mkt(1); //puts("---"); access(y), splay(y), siz[y] += siz[x]; //puts("---"); if (chl[y][0]) add[chl[y][0]] += siz[x]; } void lct_cut(int x, int y) // cut x->y { cut(x, y), mkt(1); access(y), splay(y), siz[y] -= siz[x]; if (chl[y][0]) add[chl[y][0]] -= siz[x]; } void set_flag(int x) { mkt(x), splay(x), siz[x] = 1; } int find_fa(int x) { access(x); while (!isrt(x)) x = fa[x]; return x; } int query(int nd) { mkt(nd), splay(nd); return siz[nd]; }} lct;struct SAM { int chl[MAXN*2][S], fa[MAXN*2], maxl[MAXN*2]; int top, last, root; void clear() { top = last = root = 1; } SAM() { clear(); } void push(int x) { //cout << "PUSH : " << (char)(x+'a') << endl; int p = last, np = ++top; maxl[np] = maxl[p] + 1; lct.set_flag(np); //puts("j"); while (p && !chl[p][x]) chl[p][x] = np, p = fa[p]; //puts("jj"); if (!p) fa[np] = root, lct.lct_link(np, root); else { int q = chl[p][x]; if (maxl[q] == maxl[p] + 1) fa[np] = q, lct.lct_link(np, q); else { int nq = ++top; maxl[nq] = maxl[p] + 1; memcpy(chl[nq], chl[q], sizeof chl[q]); lct.lct_link(nq, fa[q]), fa[nq] = fa[q]; lct.lct_cut(q, fa[q]), lct.lct_link(q, nq), fa[q] = nq; lct.lct_link(np, nq), fa[np] = nq; while (p && chl[p][x] == q) chl[p][x] = nq, p = fa[p]; } } //puts("jjj"); last = np; }} sam;char str[MAXN*2];int q, mask = 0;void decode(int mask){ int len = strlen(str); for (int j = 0; j < len; j++) { mask = (mask*131+j)%len; swap(str[j], str[mask]); }}void get_str(char str[]){ scanf("%s", str); decode(mask);}char opt[10];int main(){ //freopen("substring.in", "r", stdin); //freopen("substring.out","w",stdout); scanf("%d", &q); scanf("%s", str); //puts("hah"); for (char *p = str; *p != '\0'; p++) sam.push(*p-'A'); //puts("hah"); for (int i = 1; i <= q; i++) { scanf("%s", opt); //cout << opt << endl; if (opt[0] == 'A') { get_str(str); for (char *p = str; *p != '\0'; p++) sam.push(*p-'A'); } else { get_str(str); int nd = sam.root, flag = 0; for (char *p = str; *p != '\0'; p++) { if (!sam.chl[nd][*p-'A']) {flag = 1; break; } else nd = sam.chl[nd][*p-'A']; } if (flag) puts("0"); else { int ans = lct.query(nd); printf("%d\n", ans); mask ^= ans; } } } return 0;}
poj2774: Long Long Message
裸题,SAM直接碾。
#include <iostream>#include <cstdio>#include <cstring>using namespace std;const int MAXN = 200005;int chl[MAXN][26], fa[MAXN], maxl[MAXN];int top = 1, root = 1, last = 1;void push(int x){ int p = last, np = ++top; maxl[np] = maxl[p]+1; while (p && !chl[p][x]) chl[p][x] = np, p = fa[p]; if (!p) fa[np] = root; else { int q = chl[p][x]; if (maxl[q] == maxl[p]+1) fa[np] = q; else { int nq = ++top; maxl[nq] = maxl[p]+1; memcpy(chl[nq], chl[q], sizeof chl[q]); fa[nq] = fa[q], fa[q] = fa[np] = nq; while (p && chl[p][x] == q) chl[p][x] = nq, p = fa[p]; } } last = np;}char str[MAXN];int main(){ scanf("%s", str); for (char *p = str; *p != '\0'; ++p) push(*p-'a'); scanf("%s", str); int nd = root, len = 0, ans = 0; for (char *p = str; *p != '\0'; ++p) { int x = *p-'a'; if (chl[nd][x]) nd = chl[nd][x], len++; else { while (nd && !chl[nd][x]) nd = fa[nd]; if (!nd) nd = root, len = 0; else len = maxl[nd]+1, nd = chl[nd][x]; } ans = max(ans, len); } cout << ans << endl; return 0;}
[SPOJ705]不同的子串
模板复习计划,裸题。
#include <bits/stdc++.h>using namespace std;const int MAXN = 50005*2;struct SAM { int chl[MAXN][26], fa[MAXN], maxl[MAXN]; int top, root, last; void clear() { top = root = last = 1, memset(chl, 0, sizeof chl), memset(fa, 0, sizeof fa), memset(maxl, 0, sizeof maxl); } SAM() { clear(); } void push(int stat, int x) { int p = last, np = ++top; maxl[np] = maxl[p]+1; while (p && !chl[p][x]) chl[p][x] = np, p = fa[p]; if (!p) fa[np] = root; else { int q = chl[p][x]; if (maxl[q] == maxl[p]+1) fa[np] = q; else { int nq = ++top; maxl[nq] = maxl[p]+1; memcpy(chl[nq], chl[q], sizeof chl[q]); fa[nq] = fa[q], fa[q] = nq, fa[np] = nq; while (p && chl[p][x] == q) chl[p][x] = nq, p = fa[p]; } } last = np; }} sam;char str[MAXN];int main(){ freopen("subst1.in", "r", stdin); freopen("subst1.out", "w", stdout); scanf("%s", str+1); for (char *p = str+1; *p != '\0'; ++p) sam.push(sam.last, *p-'A'); long long ans = 0; for (int i = 1; i <= sam.top; i++) ans += sam.maxl[i]-sam.maxl[sam.fa[i]]; cout << ans << endl; return 0;}
bzoj2806: [Ctsc2012]Cheat
比较神的题…
后缀自动机上dp,由于dp决策有区间性质,可以用线段树或者单调队列维护。线段树版本
线段树(TLE):
#include <bits/stdc++.h>using namespace std;const int MAXN = 1100001*2;int tree[MAXN][2], fin[MAXN], col[MAXN], trie_root = 0, trie_top = 0;void push_str(int &nd, const char *str){ if (!nd) nd = ++trie_top; if (*str == '\0') fin[nd] = 1; else push_str(tree[nd][*str-'0'], str+1), col[tree[nd][*str-'0']] = *str-'0';}int chl[MAXN][2], fa[MAXN], maxl[MAXN], root = 1, top = 1;void push(int p, int x, int &last){ int np = ++top; maxl[np] = maxl[p]+1; while (p && !chl[p][x]) chl[p][x] = np, p = fa[p]; if (!p) fa[np] = root; else { int q = chl[p][x]; if (maxl[q] == maxl[p]+1) fa[np] = q; else { int nq = ++top; maxl[nq] = maxl[p]+1; chl[nq][0] = chl[q][0], chl[nq][1] = chl[q][1]; fa[nq] = fa[q], fa[q] = fa[np] = nq; while (p && chl[p][x] == q) chl[p][x] = nq, p = fa[p]; } } last = np;}queue<int> que;int stat[MAXN];void build_tree(){ que.push(trie_root), stat[trie_root] = 1; while (!que.empty()) { int tp = que.front(); que.pop(); int last; push(stat[tp], col[tp], last); for (int i = 0; i <= 1; i++) if (tree[tp][i]) stat[tree[tp][i]] = last, que.push(tree[tp][i]); }}bool match(char str[], int l, int r){ if (l <= 0 || l > r) return 0; int nd = root, ans = 0, len = 0; for (int i = l; i <= r; i++) { int x = str[i]-'0'; if (chl[nd][x]) nd = chl[nd][x], len++; else { while (nd && !chl[nd][x]) nd = fa[nd]; if (!nd) nd = root, len = 0; else len = maxl[nd]+1, nd = chl[nd][x]; } ans = max(ans, len); } return ans == r-l+1;}char str[MAXN];int dp[MAXN], max_back[MAXN];int n, m;int zkw[(1<<21)+1], N = 1<<20;void modify(int nd, int val){ nd += N-1; zkw[nd] = val; for (int i = nd>>1; i; i >>= 1) zkw[i] = max(zkw[i*2], zkw[i*2+1]);}int ask_max(int l, int r){ if (l > r) return 0; int ans = -233333333; for (l += N-1, r += N-1; l < r; l>>=1, r>>=1) { if (l&1) ans = max(ans, zkw[l++]); if (!(r&1)) ans = max(ans, zkw[r--]); } if (l == r) ans = max(ans, zkw[l]); return ans;}bool do_dp(int L){ int l = strlen(str+1); memset(dp, 0, sizeof dp); memset(zkw, -127/3, sizeof zkw); int max_val = 0; modify(0, 0); for (int i = 1; i <= l; i++) { dp[i] = max_val; if (i-L >= 0 && max_back[i] <= i-L) dp[i] = max(dp[i], i+ask_max(max_back[i], i-L)); max_val = max(max_val, dp[i]); modify(i, dp[i]-i); } return dp[l]*10 >= l*9;}int main(){ scanf("%d%d", &n, &m); for (int i = 1; i <= m; i++) { scanf("%s", str); push_str(trie_root, str); } build_tree(); for (int i = 1; i <= n; i++) { scanf("%s", str+1); memset(max_back, 0, sizeof max_back); for (int i = 1, l = strlen(str+1); i <= l; i++) { int lf = 1, rt = i, mid; while (lf <= rt) { mid = (lf+rt)>>1; if (match(str, i-mid+1, i)) lf = mid+1; else rt = mid-1; } max_back[i] = i-(lf-1); } int l = 1, r = strlen(str+1), mid; while (l <= r) { mid = (l+r)>>1; if (do_dp(mid)) l = mid+1; else r = mid-1; } printf("%d\n", l-1); } return 0;}
- 后缀自动机学习笔记
- 后缀自动机学习笔记
- 后缀自动机学习笔记3
- [Notes] 后缀自动机学习笔记
- [学习笔记] 后缀自动机学习笔记
- 后缀自动机(SAM)学习笔记
- SAM 后缀自动机——学习笔记
- 【字符串数据结构后缀系列Part2】后缀自动机学习笔记
- 后缀自动机 笔记
- 后缀自动机学习资料
- 后缀自动机学习
- 后缀自动机学习
- 后缀自动机学习
- 后缀自动机学习
- 后缀自动机学习总结
- 后缀自动机学习小记
- 后缀自动机学习资料
- 学习后缀自动机想法
- PHP获得真实客户端的真实IP
- JAVA中的冒泡排序和选择排序
- string初始化 n种方法
- oj122. Best Time to Buy and Sell Stock II
- Android实现侧滑(SlidingMenu)
- 后缀自动机学习笔记
- 【TensorFlow问题】AttributeError:'module' object has no attribute 'mul'
- TCP、UDP、IP 协议分析
- VS 运行网站报:HTTP Error 500.22
- Array.prototype.filter()
- Neutron采用Openvswitch通用配置【ocata】
- 转发一篇写得不错的关于iptables的文章
- galang 学习之grpc+ protobuf(一)
- HashMap和HashSet的区别