【ybtoj高效进阶 21168】打字机器(Trie树)(LCA)(值域线段树)

打字机器

题目链接:ybtoj高效进阶 21168

题目大意

要你实现一些操作:
在末尾加字符或删字符,记录当前的字符串。
将记录的第 i 个字符串的第 j 个字符设为不可匹配字符,即它不可以与任何字符匹配,两个不可匹配字符和不可以匹配。(如果这个位置原来就是,就改回之前的字符)
求两个记录下来的字符串的最长公共前缀。

思路

首先是找到那些记录的字符串,这个可以用 Trie 树很快的实现。

然后你考虑匹配,如果没有不可匹配字符。
那就是直接是 Trie 树上的 LCA 的深度。

然后就是有不可匹配字符,那就是两个字符串分别找到它们第一个不可匹配字符(如果没有就是在最后一个)
现在一想好像可删堆(支持删除的单调栈)这些都可以,但我当时用了值域线段树。

然后就好啦。

代码

#include<cstdio> #include<iostream> #include<algorithm> using namespace std; struct Trie { int son[31], fa[22], deg; }a[500001]; int n, op, x, y, tot, pls[500001], now; int b[500001], bn, opp[500001], c[500001]; char cc; int LCA(int x, int y) { if (a[x].deg < a[y].deg) swap(x, y); for (int i = 20; i >= 0; i--) if (a[a[x].fa[i]].deg >= a[y].deg) x = a[x].fa[i]; if (x == y) return x; for (int i = 20; i >= 0; i--) if (a[x].fa[i] != a[y].fa[i]) x = a[x].fa[i], y = a[y].fa[i]; return a[x].fa[0]; } struct XDtree {//值域线段树 int ls[500001 << 6], rs[500001 << 6], tott, root[500001]; bool a[500001 << 6]; void up(int now) { if (ls[now] && a[ls[now]]) {a[now] = 1; return ;} if (rs[now] && a[rs[now]]) {a[now] = 1; return ;} a[now] = 0; } int insert(int now, int l, int r, int pl) { if (!now) now = ++tott; if (l == r) { a[now] ^= 1; return now; } int mid = (l + r) >> 1; if (pl <= mid) ls[now] = insert(ls[now], l, mid, pl); else rs[now] = insert(rs[now], mid + 1, r, pl); up(now); return now; } int get_fir(int now, int l, int r) { if (!now) return -1; if (!a[now]) return -1; if (l == r) return l; int mid = (l + r) >> 1; int x = get_fir(ls[now], l, mid); if (x == -1) return get_fir(rs[now], mid + 1, r); else return x; } }T; int main() { // freopen("typewriter.in", "r", stdin); // freopen("typewriter.out", "w", stdout); // printf("%.3lf", (sizeof(a) + sizeof(pls) * 5 + sizeof(T)) / 1024.0 / 1024.0); scanf("%d", &n); now = 1; tot = 1; while (n--) { scanf("%d", &op); if (op == 1) { cc = getchar(); while (cc < 'a' || cc > 'z') cc = getchar(); if (a[now].son[cc - 'a']) now = a[now].son[cc - 'a']; else {//建 Trie 树 a[now].son[cc - 'a'] = ++tot; a[tot].fa[0] = now; a[tot].deg = a[now].deg + 1; now = tot; } } if (op == 2) { now = a[now].fa[0]; } if (op == 3) { b[++bn] = now; opp[bn] = 3; } if (op == 4) { scanf("%d %d", &x, &y); b[++bn] = x; c[bn] = y; opp[bn] = 4; } if (op == 5) { scanf("%d %d", &x, &y); b[++bn] = x; c[bn] = y; opp[bn] = 5; } } for (int i = 1; i <= 20; i++)//建 Trie 树上的倍增数组,准备 LCA for (int j = 1; j <= tot; j++) a[j].fa[i] = a[a[j].fa[i - 1]].fa[i - 1]; for (int i = 1; i <= bn; i++) { if (opp[i] == 3) { pls[++pls[0]] = b[i]; } if (opp[i] == 4) { T.root[b[i]] = T.insert(T.root[b[i]], 1, a[pls[b[i]]].deg, c[i]); } if (opp[i] == 5) { int ans = a[LCA(pls[b[i]], pls[c[i]])].deg;//如果没有不可匹配就是 LCA 的深度 int x = T.get_fir(T.root[b[i]], 1, a[pls[b[i]]].deg);//找到两边早的不可匹配 if (x == -1) x = a[pls[b[i]]].deg + 1; int y = T.get_fir(T.root[c[i]], 1, a[pls[c[i]]].deg); if (y == -1) y = a[pls[c[i]]].deg + 1; ans = min(ans, min(x, y) - 1); printf("%d\n", ans); } } return 0; }

__EOF__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/YBTOJ_GXJJ_21168.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(52)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示