【后缀自动机或二分】 HDU 5431 AB String
题意:给出只有AB组成的字符串S,求第k个不在S中出现的串T。
思路:我们可以把原串中logK左右的串都拿出来排序。然后直接二分答案求解即可
代码:
#include <cstdio> #include <string> #include <cstring> #include <vector> #include <algorithm> using namespace std; typedef long long ll; const int MAX_N = 20005; char s[MAX_N]; vector<int> val(MAX_N); inline void solve() { scanf("%s", s); int sz = strlen(s); fill(val.begin(), val.end(), 0); vector<ll> h[35]; vector<ll> sm(35, 0); for (int i = 1; i <= 32; ++i) { for (int j = 0; j + i - 1 < sz; ++j) { val[j] = (val[j] << 1) + (s[j + i - 1] - 'A'); h[i].push_back(val[j]); } sort(h[i].begin(), h[i].end()); h[i].resize(unique(h[i].begin(), h[i].end()) - h[i].begin()); sm[i] = sm[i - 1] + ((1ll << i) - h[i].size()); } int N; ll K; scanf("%d", &N); while (N--) { scanf("%lld", &K); int pos = lower_bound(sm.begin(), sm.end(), K) - sm.begin(); ll l = 0, r = (1ll << pos) - 1; while (l <= r) { ll mid = (l + r) / 2; int idx = upper_bound(h[pos].begin(), h[pos].end(), mid) - h[pos].begin(); ll cnt = sm[pos - 1] + mid + 1ll - idx; if (cnt < K) l = mid + 1; else if (cnt > K) r = mid - 1; else if (idx > 0 && h[pos][idx - 1] == mid) r = mid - 1; else { string res; for (int j = 0; j < pos; ++j) { if ((1ll << j) & mid) res = "B" + res; else res = "A" + res; } printf("%s\n", res.c_str()); break; } } } } int main() { int T; scanf("%d", &T); while (T--) { solve(); } return 0; }
思路:设F[i][j]表示到SAM上的状态i,还有长度j时有多少可能的T串数量
以下是后缀自动机代码:
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; typedef long long ll; const int MAX_N = 20007; struct SAM { int val[MAX_N], fa[MAX_N], c[2][MAX_N]; int tot, last; ll dp[MAX_N][33]; inline int newNode(int step) { val[++tot] = step; fa[tot] = 0; for (int i = 0; i < 2; ++i) c[i][tot] = 0; return tot; } inline void extend(int k) { int p = last; int np = newNode(val[last] + 1); while (p && !c[k][p]) c[k][p] = np, p = fa[p]; if (!p) fa[np] = 1; else { int q = c[k][p]; if (val[q] == val[p] + 1) fa[np] = q; else { int nq = newNode(val[p] + 1); for (int i = 0; i < 2; ++i) c[i][nq] = c[i][q]; fa[nq] = fa[q]; fa[q] = fa[np] = nq; while (p && c[k][p] == q) c[k][p] = nq, p = fa[p]; } } last = np; } inline int add(int k) { extend(k); } inline void init() { tot = 0; last = newNode(0); } } suf; char str[MAX_N]; int c[MAX_N], sa[MAX_N]; int main() { int T;scanf("%d", &T); while (T-- > 0) { scanf("%s", str); int len = strlen(str); suf.init(); for (int i = 0; str[i]; ++i) suf.add(str[i] - 'A'); memset(c, 0, sizeof c); for (int i = 1; i <= suf.tot; ++i) ++c[suf.val[i]]; for (int i = 1; i <= len; ++i) c[i] += c[i - 1]; for (int i = 1; i <= suf.tot; ++i) sa[c[suf.val[i]]--] = i; for (int i = suf.tot; i > 0; --i) { int id = sa[i]; suf.dp[id][0] = 0; for (int k = 1; k < 33; ++k) { suf.dp[id][k] = 0; for (int j = 0; j < 2; ++j) { if (suf.c[j][id]) suf.dp[id][k] += suf.dp[suf.c[j][id]][k - 1]; else suf.dp[id][k] += (1ll << (k - 1)); } } } int n; scanf("%d", &n); while (n--) { ll K; scanf("%I64d", &K); int depth = 0; ll now = 0; for (int i = 1; i < 33; ++i) { now += suf.dp[1][i]; if (now >= K) { depth = i; break; } } for (int i = 1; i < depth; ++i) K -= suf.dp[1][i]; int id = 1; while(depth > 0) { ll t0 = 0; if (suf.c[0][id]) t0 = suf.dp[suf.c[0][id]][depth - 1]; else t0 = 1ll << (depth - 1); if(K <= t0) { if (suf.c[0][id]) { --depth; id = suf.c[0][id]; putchar('A'); } else { --K; putchar('A'); for(int i = depth - 2; i >= 0; --i) if(K & (1LL << i)) putchar('B'); else putchar('A'); break; } } else { K -= t0; if (suf.c[1][id]) { --depth; id = suf.c[1][id]; putchar('B'); } else { --K; putchar('B'); for(int i = depth - 2; i >= 0; --i) if(K & (1LL << i)) putchar('B'); else putchar('A'); break; } } } puts(""); } } return 0; }