Illublog我多想说再见啊

CF1037H Security

Illu·2022-02-18 20:50·16 次阅读

CF1037H Security

Description#

给定一个文本串 s 和询问次数 q ,每次给定小文本串 t 和一个区间 lr ,求一个字典序尽可能小的 s 使得是 s 的子串并且字典序严格大于 t

|s|, |t|2105,  q2 cdot105

Solution#

好像对 s 的要求有点多,那就以最麻烦的子串作为出发条件。

会发现这题能和 你的名字 套路极度地像。同样,先考虑询问全是 s 全串怎么办。

situation 1#

既然要求字典序必须要严格大于 t 但是最小,所以答案要尽可能贴近 t 的样子,就相当于 t 的一个前缀加上一个 s 上有转移的,加完比 t 大的字符。

所以对 s 建立 SAM ,然后我们直接把 t 丢上去跑匹配,匹配不上就看能不能加一个更大的字符(如果 t 也是 s 的子串就看能不能额外加一个字符),如果没有就往前一位上再找,假如这样子还没有答案那肯定就是无解了。

situation 2#

如果是区间的话,发现我们好像需要 endpos ,不然可能一些本不在区间内的位置会匹配上。

那就还是用线段树合并解决 endpos 问题,每次找匹配,找字符都查询一遍是否合法,准确点是左端点在 L 右边,右端点在 R 左边才对。

插一嘴#

刚想着要继续说什么,但是发现这样子就已经做完了。。甚至感觉 SAM 有点算是大材小用了吗。。

不!

这是线段树合并维护 endpos 的板子(迫真

Code#

Code
Copy
/* */ #include using namespace std; typedef long long ll; const int N = 4e5 + 10, M = 4e6 + 10; int rt[N], tot, ls[M], rs[M], nxt[N]; inline int read() { char ch = getchar(); int s = 0, w = 1; while (!isdigit(ch)) {if (ch == '-') w = -1; ch = getchar();} while (isdigit(ch)) {s = (s << 3) + (s << 1) + (ch ^ 48); ch = getchar();} return s * w; } inline void modify(int &now, int lt, int rt, int it) { if (!now) now = ++tot; if (lt == rt) return ; int mid = (lt + rt) >> 1; if (it <= mid) modify(ls[now], lt, mid, it); else modify(rs[now], mid + 1, rt, it); } inline bool query(int now, int lt, int rt, int L, int R) { if (!now) return 0; if (L <= lt && rt <= R) return 1; int mid = (lt + rt) >> 1, res = 0, ret = 0; if (L <= mid) res = query(ls[now], lt, mid, L, R); if (R > mid) ret = query(rs[now], mid + 1, rt, L, R); return res | ret; } inline int merge(int u, int v, int lt, int rt) { if (!u) return v; if (!v) return u; if (lt == rt) return u | v; int mid = (lt + rt) >> 1, w = ++tot; ls[w] = merge(ls[u], ls[v], lt, mid); rs[w] = merge(rs[u], rs[v], mid + 1, rt); return w; } struct SAM { int n, cnt, las, len[N], link[N], ch[N][26]; char s[N]; int tong[N], rk[N], mx[N]; inline void init() { cnt = las = 1; memset(ch[1], 0, sizeof(ch[1])); } inline void SAM_stru(int c) { int cur = ++cnt, p = las; memset(ch[cur], 0, sizeof(ch[cur])); las = cur; len[cur] = len[p] + 1; while (p && !ch[p][c]) ch[p][c] = cur, p = link[p]; if (!p) {link[cur] = 1; return ;} int q = ch[p][c]; if (len[p] + 1 == len[q]) {link[cur] = q; return ;} int clo = ++cnt; link[clo] = link[q]; len[clo] = len[p] + 1; link[q] = link[cur] = clo; memcpy(ch[clo], ch[q], sizeof(ch[clo])); while (p && ch[p][c] == q) ch[p][c] = clo, p = link[p]; } inline void Tong_sort() { for (int i = 1; i <= cnt; ++i) ++tong[len[i]]; for (int i = 1; i <= cnt; ++i) tong[i] += tong[i - 1]; for (int i = 1; i <= cnt; ++i) rk[tong[len[i]]--] = i; for (int i = cnt, v, u; i >= 1; --i) { v = rk[i]; u = link[v]; rt[u] = merge(rt[u], rt[v], 0, n - 1); } } } s, t; inline void mian() { int l = read() - 1, r = read() - 1; scanf("%s", t.s); t.n = strlen(t.s); int v = 1, it = 0; for (int i = 0, c; i <= t.n; ++i, it = i) { nxt[i] = -1; c = t.s[i] - 'a'; for (int j = max(c + 1, 0), u; j < 26; ++j) { u = s.ch[v][j]; if (u && query(rt[u], 0, s.n - 1, l + i, r)) { nxt[i] = j; break; } } if (i == t.n || !s.ch[v][c] || !query(rt[s.ch[v][c]], 0, s.n - 1, l + i, r)) break; v = s.ch[v][c]; } while (~it && nxt[it] == -1) --it; if (it == -1) return printf("-1\n"), void(); for (int i = 0; i < it; ++i) cout << t.s[i]; cout << (char)(nxt[it] + 'a') << "\n"; } int main() { scanf("%s", s.s); s.n = strlen(s.s); s.init(); for (int i = 0; i < s.n; ++i) { s.SAM_stru(s.s[i] - 'a'); modify(rt[s.las], 0, s.n - 1, i); } s.Tong_sort(); int T = read(); while (T--) mian(); return 0; }
posted @   Illusory_dimes  阅读(16)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端
点击右上角即可分享
微信分享提示
目录