不多说了,直接看题吧。
哦对,由于本人极其厌恶 SAM,所以大部分题都是拿 SA 写的,但会提供 SAM 思路(有时候也会提供 SA 思路)。
P3804 【模板】后缀自动机(SAM)
模板题,在 Parent 树上树形 DP 即可。
P3975 [TJOI2015] 弦论
如果 \(t = 0\),那么万事大吉。但是我们有万恶的 \(t = 1\),所以还得处理一下每个等价类的大小。
这题是真恶心,先是 WA 了所有 \(t = 0\),然后搞定 \(t = 0\) 后又 WA 了所有 \(t = 1\),哎。
Code
#include <bits/stdc++.h>
#define int long long
using namespace std;const int N = 2e6 + 10;
int n, t, k;
int la, tot;
int le[N], lk[N];
int ch[N][26];
int f[N], g[N];
bool vis[N];
vector <int> tr[N];
string s, ans;void insert(string s, int n) {lk[0] = -1;for (int i = 0; i < n; i++) {int x = s[i] - 'a', p = la, cur = ++tot; le[cur] = le[p] + 1;f[cur] = 1;while (p != -1 && !ch[p][x]) {ch[p][x] = cur;p = lk[p];}if (p == -1) lk[cur] = 0;else {int q = ch[p][x];if (le[q] == le[p] + 1) lk[cur] = q;else {int cp = ++tot;le[cp] = le[p] + 1, lk[cp] = lk[q];for (int j = 0; j < 26; j++) {ch[cp][j] = ch[q][j];}while (p != -1 && ch[p][x] == q) {ch[p][x] = cp;p = lk[p];}lk[q] = lk[cur] = cp;}}la = cur;}for (int i = 1; i <= tot; i++) {tr[lk[i]].push_back(i);}
}
void dfs1(int u) {for (int i = 0; i < tr[u].size(); i++) {int v = tr[u][i];dfs1(v);f[u] += f[v];}
}
void dfs2(int u) {if (vis[u]) return ;vis[u] = 1;for (int i = 0; i < 26; i++) {int v = ch[u][i];//cout << v << ' ';if (!v) continue;dfs2(v);g[u] += g[v];}
}
void dfs3(int u, int k) {//cout << endl << f[u] << endl;if (k <= f[u]) return ;k -= f[u];for (int i = 0; i < 26; i++) {int v = ch[u][i];if (!v) continue;if (k > g[v]) k -= g[v];else {cout << char(i + 'a');dfs3(v, k);return ;}}
}signed main() {//freopen("1.in", "r", stdin);cin >> s >> t >> k;assert(t == 1);n = s.length();insert(s, n);dfs1(0);f[0] = 0;for (int i = 1; i <= tot; i++) {if (!t) f[i] = g[i] = 1;else g[i] = f[i];}dfs2(0);if (g[0] < k) {cout << -1 << "\n";return 0;}dfs3(0, k);//cout << ans;return 0;
}
SP1811 LCS - Longest Common Substring
对 \(S\) 建出 SAM,在 \(S\) 中匹配 \(T\),失配则跳后缀链接,最后取 \(\max\) 即可。
先粘个 SA 代码,SAM 代码以后再补。
Code
#include <bits/stdc++.h>
using namespace std;const int N = 2e6 + 10;
int tn, sn, a[N], cf[N];
int n, m, c[N], ans;
int sa[N], rk[N], ht[N];
int b[N], tp[N];
int col[N], ct[N], cnt;
int q[N], hd = 1, tl;
char ts[N];void f_sort() {for (int i = 1; i <= m; i++) b[i] = 0;for (int i = 1; i <= n; i++) b[rk[i]]++;for (int i = 1; i <= m; i++) b[i] += b[i - 1];for (int i = n; i; i--) sa[b[rk[tp[i]]]--] = tp[i];
}
void g_sa() {for (int i = 1; i <= n; i++) rk[i] = c[i], tp[i] = i;f_sort();int p = 0;for (int w = 1; w <= n; w <<= 1) {if (p == n) break;p = 0;for (int i = n - w + 1; i <= n; i++) tp[++p] = i;for (int i = 1; i <= n; i++) {if (sa[i] > w) tp[++p] = sa[i] - w;}f_sort(), swap(rk, tp), rk[sa[1]] = p = 1;for (int i = 2; i <= n; i++) {int sl = sa[i - 1], sr = sa[i];if (tp[sl] == tp[sr] && tp[sl + w] == tp[sr + w]) {rk[sa[i]] = p;} else rk[sa[i]] = ++p;}m = p;}
}
void g_hei() {int k = 0;for (int i = 1; i <= n; i++) {if (!rk[i]) continue;if (k) k--;while (c[i + k] == c[sa[rk[i] - 1] + k]) ++k;ht[rk[i]] = k;}
}int main() {m = 1e5, tn = 2;for (int i = 1; i <= tn; i++) {scanf("%s", ts + 1);int sn = strlen(ts + 1);for (int j = 1; j <= sn; j++) {c[++n] = ts[j], col[n] = i;}c[++n] = m - i; }g_sa(), g_hei();ht[1] = 1e9;int l = 1, r = 0;while (l <= n) {#define cr col[sa[r]]#define cl col[sa[l]]while (r < n && cnt < tn) {if (col[sa[++r]]) cnt += !ct[cr], ct[cr]++;while (hd <= tl && ht[q[tl]] >= ht[r]) tl--;q[++tl] = r;}if (cnt < tn) break;ans = max(ans, ht[q[hd]]);if (col[sa[l]]) ct[cl]--, cnt -= !ct[cl];while (hd <= tl && q[hd] == l + 1) hd++;l++;#undef cr#undef cl}cout << ans;return 0;
}
P6640 [BJOI2020] 封印
考虑和上一题一样的思路,在 \(t\) 中匹配 \(s\),你会发现你可以求出 \(s\) 的每个前缀在 \(t\) 中的最长后缀,记为 \(f_i\)。
然后你就会发现答案形如:
\[\max_{i = l} ^ r \min \{ f_i, i - l + 1 \}
\]
直接二分答案即可。
我实在不想写万恶的 SAM 了,所以这题也用 SA 了。
Code
#include <bits/stdc++.h>
using namespace std;const int N = 1e6 + 10;
const int I = 1e9 + 10;
int n, m, q, l1, l2;
int sa[N], rk[N], b[N], tp[N];
int ht[N], f[N][20], g[N], lg[N];
char c[N], c1[N], c2[N];void f_sort() {for (int i = 1; i <= m; i++) b[i] = 0;for (int i = 1; i <= n; i++) b[rk[i]]++;for (int i = 1; i <= m; i++) b[i] += b[i - 1];for (int i = n; i; i--) sa[b[rk[tp[i]]]--] = tp[i];
}
void g_sa() {for (int i = 1; i <= n; i++) rk[i] = c[i], tp[i] = i;f_sort();int p = 0;for (int w = 1; w <= n; w <<= 1) {if (p == n) continue;p = 0;for (int i = n - w + 1; i <= n; i++) tp[++p] = i;for (int i = 1; i <= n; i++) {if (sa[i] > w) tp[++p] = sa[i] - w;}f_sort(), swap(rk, tp), rk[sa[1]] = p = 1;for (int i = 2; i <= n; i++) {int sl = sa[i - 1], sr = sa[i];if (tp[sl] == tp[sr] && tp[sl + w] == tp[sr + w]) {rk[sa[i]] = p;} else rk[sa[i]] = ++p;}m = p;}
}
void g_hei() {int k = 0;for (int i = 1; i <= n; i++) {if (!rk[i]) continue;if (k) k--;while (c[i + k] == c[sa[rk[i] - 1] + k]) ++k;ht[rk[i]] = k;}
}
void g_as() {for (int i = 1; i <= n; i++) {if (sa[i] <= l1) continue;int lcp = I;for (int j = i + 1; j <= n; j++) {if (sa[j] > l1) break;g[sa[j]] = lcp = min(lcp, ht[j]);}}for (int i = n; i; i--) {if (sa[i] <= l1) continue;int lcp = I;for (int j = i - 1; j; j--) {if (sa[j] > l1) break;g[sa[j]] = max(g[sa[j]], lcp = min(lcp, ht[j + 1]));}}
}
void init() {lg[0] = -1;for (int i = 1; i <= n; i++) lg[i] = lg[i >> 1] + 1;for (int i = 1; i <= n; i++) f[i][0] = g[i];for (int j = 1; j <= lg[n]; j++) {for (int i = 1; i + (1 << j) - 1 <= n; i++) {f[i][j] = max(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);}}
}
int qry(int l, int r) {int k = lg[r - l + 1];return max(f[l][k], f[r - (1 << k) + 1][k]);
}
int solve(int l, int r) {if (l > r) swap(l, r);int L = 0, R = r - l + 1;while (L < R) {int M = (L + R + 2) >> 1;//cout << l << ' ' << r - M + 1 << ' ' << qry(l, r - M + 1) << endl;if (qry(l, r - M + 1) >= M) L = M;else R = M - 1;}return L;
}int main() {cin >> c1 + 1 >> c2 + 1;l1 = strlen(c1 + 1), l2 = strlen(c2 + 1);//cout << l1 << ' ' << l2 << ' ';for (int i = 1; i <= l1; i++) c[++n] = c1[i];for (int i = 1; i <= l2; i++) c[++n] = c2[i];m = 127;g_sa(), g_hei(), g_as(), init();cin >> q; while (q--) {int l, r;cin >> l >> r;cout << solve(l, r) << "\n";} return 0;
}