当前位置: 首页 > news >正文

题解:Atcoder Weekday Contest awc0110 E - Abbreviation Assignment

题目描述

高桥正在开发一个供公司内部使用的联系人列表系统。每个员工都有一个唯一的用户名,但由于用户名较长,他决定为每个员工分配一个缩写

公司有 $N$ 名员工,其中 $i$ 名员工的用户名是一个由小写英文字母组成的字符串 $S_i$ 。对于每个员工 $i$ ,我们会从他们的用户名 $S_i$ 中选择一个非空前缀,并将其指定为缩写 $T_i$ 。也就是说, $T_i$ 必须是由 $S_i$ 的前 $1$ 至 $|S_i|$ 个字符组成的字符串。也允许使用整个用户名作为缩写。

缩写赋值 $(T_1, T_2, \ldots, T_N)$ 必须满足以下无前缀条件

- 无前缀条件:对于任意两个不同的员工 $i, j$ ( $i \neq j$ )( $i \neq j$ ) 中, $T_i$ 不是 $T_j$ 的前缀,且 $T_j$ 不是 $T_i$ 的前缀。

这里,如果 $|A| \leq |B|$ 和 $B$ 的第一个 $|A|$ 字符与 $A$ 匹配,那么字符串 $A$ 就是字符串 $B$ 的前缀。其中,当 $A = B$ 时, $A$ 也是 $B$ 的前缀。

因此,根据无前缀条件,也不允许有 $T_i = T_j$ 为不同的雇员 $i, j$ ( $i \neq j$ )。

如果存在有效的缩写分配,高桥希望最小化缩写的总长度 $\sum_{i=1}^{N} |T_i|$ 。如果存在有效的赋值,则输出最小总长度;否则,输出 $-1$ 。

解题思路

问题转化

每个员工需要选择一个自己用户名的非空前缀作为缩写,且所有缩写之间不能存在前缀关系。

核心观察:如果员工A的用户名是员工B的用户名的真前缀,那么必然无解。因为A可以选择的最短缩写是它自己的完整用户名(长度为|A|),而B的任何前缀都必须以A为前缀(B的前缀至少包含到A的末尾),这样就一定存在前缀关系。

Trie树的构建

将所有用户名插入到Trie树中,其中:

  • 每个节点代表一个字符串(从根到该节点的路径)

  • is_end[u] = true表示该节点对应的字符串是一个完整的用户名

可行性判断

对于每个用户名S,检查其所有真前缀中是否存在另一个用户名:

  • 如果存在,则无解,输出-1

  • 否则,所有用户名之间没有一个是另一个的真前缀

最小化总长度

当可行性满足后,我们需要为每个用户名选择一个满足条件的节点(Trie中的某个节点,且该节点是用户名路径上的节点),使得总深度最小。

贪心策略:对于每个用户名,选择尽可能浅的节点,但该节点的子树中只能包含这一个用户名。

推导

  • 如果某个节点v(假设对应字符串P)的子树中只包含一个用户名(即cnt[v] == 1),那么该用户名可以选择P作为缩写,且不会与其他用户名产生前缀冲突

  • 为了让每个用户名的缩写尽量短,我们找最浅的满足cnt[v] == 1的节点

实现细节

  1. 计算每个节点的cnt:该节点子树中包含的用户名数量

  2. 对于每个用户名对应的终端节点u,沿着父节点向上找,直到遇到cnt[v] != 1的节点

  3. 假设停在节点v,那么该用户名的最佳缩写是v的一个子节点(深度为depth[v] + 1

  4. 累加所有缩写的长度

完整代码

#include<bits/stdc++.h> using namespace std; const int MAX = 500010; int trie[MAX][26]; bool is_end[MAX]; int parentArr[MAX], depthArr[MAX], cnt[MAX]; int tot=0; void insert(const string &s) { int u = 0; for (char ch : s) { int c = ch - 'a'; if (!trie[u][c]) { trie[u][c] = ++tot; parentArr[tot] = u; depthArr[tot] = depthArr[u] + 1; } u = trie[u][c]; } is_end[u] = true; } bool check(const string &s) { int u = 0; for (int i = 0; i < (int)s.size() - 1; ++i) { int c = s[i] - 'a'; u = trie[u][c]; if (is_end[u]) return false; } return true; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int N; cin >> N; vector<string> str(N); for (int i = 0; i < N; ++i) { cin >> str[i]; insert(str[i]); } for (const string &s : str) { if (!check(s)) { cout << -1 << '\n'; return 0; } } for (int i = 0; i <= tot; ++i) { cnt[i] = is_end[i] ? 1 : 0; } for (int i = tot; i >= 1; --i) { cnt[parentArr[i]] += cnt[i]; } long long ans = 0; for (int u = 0; u <= tot; ++u) { if (!is_end[u]) continue; int v = u; while (v != 0 && cnt[v] == 1) { v = parentArr[v]; } ans += depthArr[v] + 1; } cout << ans << '\n'; return 0; }
http://www.cnnetsun.cn/news/3302239.html

相关文章:

  • Windows 11 / macOS Sonoma 系统下:3 款主流杀软对 10 类木马的查杀率实测对比
  • YOLOv8环境配置与自定义数据集训练全流程实战指南
  • MongoDB 删除操作深度对比:deleteOne、deleteMany、remove 与 drop 的 4 种场景选择指南
  • VCS 后仿违例排查实战:8步定位法从报错到根因分析与解决
  • char与 unsigned char类型变量打印,注意事项
  • 提示词的三个基本要素:指令、上下文与输入
  • TSP质量计划与PSP个人指标:3个核心指标(Yield/PQI/AFR)驱动缺陷预防
  • 腾讯云轻量应用服务器 2025 版:宝塔面板 10 分钟部署 WordPress 个人博客
  • 辐射4 Ubuntu鼠标指针MOD制作教程:从素材提取到游戏界面美化
  • Node.js 浏览器引擎 + Python 大脑:Playwright 混合架构爬虫系统深度解析
  • KUKA后台程序排错指南:SPS循环中3类常见逻辑错误与修复方案
  • 深度学习模型优化:超参数调优、正则化与优化算法实战指南
  • NLP情感分析实战:从词向量到BiLSTM的IMDB影评分类
  • ECharts 5.x 坐标轴样式深度定制:3类属性与5个实战场景解析
  • ZW32-12 弹簧操动机构维护:3类常见机械故障诊断与预防性检查清单
  • 基于springboot的旅游出行指南
  • 深度学习调优实战:从训练稳定性到正则化策略
  • 校园网路由器配置 2024:电信宽带学号@ctc认证,3步解决WiFi共享
  • TDA7468与PIC18F97J94构建的高性能音频处理系统
  • 物理层编码实战:4种编码方式(NRZ/RZ/曼彻斯特/差分曼彻斯特)波形对比与Python仿真
  • 阿里 Qoder 系列产品最新况介绍,新推出产品免费试用,还有多种优惠活动等你体验!
  • 第八章:字符串与字符串函数
  • TC78H651AFNG与PIC18F97J94的直流有刷电机驱动方案
  • Photoshop 动作录制:1分钟自动化生成1寸/2寸证件照排版(附动作文件)
  • Vulnhub 靶机 RickdiculouslyEasy:3种非常规端口利用与2个隐蔽后门分析
  • PyTorch深度学习3小时入门:从动态计算图到图像识别实战
  • Tomasulo 算法模拟器实验:3种数据相关(RAW/WAW/WAR)的消除过程与状态追踪
  • Linux Bridge 与 veth-pair 实战:3步构建K8s Pod网络命名空间隔离与通信
  • 计算机视觉完整学习路径:从CNN到Transformer的实战指南
  • 基于TC78H651AFNG和STM32的直流电机驱动方案