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

题解:CF566A Matching Names

题意

\(n\) 名同学,每位同学有一个真名与一个笔名,都是一个由小写英文字母组成的字符串。每位同学可以选择一位同学,使自己的真名与那位同学的笔名相匹配,产生的贡献为其最长公共前缀,每位同学的笔名只能与一人匹配,求贡献总和的最大值。

思路

把笔名插入字典树,并在每个结束点 \(i\) 打上标记,标记位置 \(i\) 到根组成的字符串是某个同学的笔名。

然后枚举每个人的真名,找字典树中所有笔名中的最长公共前缀,在最长前缀的末尾 \(i\) 打上标记,标记位置 \(i\) 到根组成的字符串是某个同学的真名与所有笔名的最长公共前缀。

最后遍历一遍字典树,进行后序遍历,若搜索至位置 \(i\),若位置 \(i\) 上有同学的笔名标记,加入栈中。若位置 \(i\) 上有同学的真名标记,如果栈非空,就用栈内的记录的同学与之配对,其公共前缀的长度为当前节点的深度。反之,将标记上放到其父节点中,即缩小某同学的真名与所有笔名的最长公共前缀长度。

但存在一定的问题,如果已经搜索了子树 \(x\),进行操作后栈不为空,随后又搜索了子树 \(y\),且 \(y\) 的深度不低于 \(x\)。若子树 \(y\) 中存在同学的真名标记,就会与栈内的元素配对,而这样计算的答案就是错误的,实际匹配的贡献值应当更小。因此,在进行更深的搜索时应清空栈,但回溯后又需要还原栈的历史状态,很简单,记录搜索到当前节点时的栈大小,让搜索完当前子树后的栈大小与之作差就可以得到实际的栈大小,同时保留剩余的栈元素。

代码

#include<bits/stdc++.h>
using namespace std;
const int N = 8e5 + 5 , M = 2e5 + 5;
int n;
string s[M];
int trie[N][27] , tot , ans[M] , sta[M] , top , h = 0;
vector<int>w[N] , vis[N];
void insert(string x , int val) {int i = 0;for(auto v : x) {int k = v - 'a';if(!trie[i][k]) trie[i][k] = ++ tot;i = trie[i][k];}vis[i].push_back(val); // 标记位置 $i$ 到根组成的字符串是某个同学的笔名。
}
void find(string x , int val) {int i = 0;for(auto v : x) {int k = v - 'a';if(!trie[i][k]) break;i = trie[i][k];}w[i].push_back(val); // 标记位置 $i$ 到根组成的字符串是某个同学的真名与所有笔名的最长公共前缀。
}
void dfs(int i , int fa , int dep , int tp) {for(auto v : vis[i]) {sta[++ top] = v; // 把当前位置的笔名标记加入栈中}for(int v = 0 ; v < 26 ; v ++) {if(!trie[i][v]) continue;dfs(trie[i][v] , i , dep + 1 , top);}for(int j = w[i].size() - 1 ; j >= 0 ; j --) {int v = w[i][j];if(top - tp > 0) { // 若栈不为空ans[v] = sta[top --]; // 匹配h += dep;	// 计算答案} else { // 若栈为空w[fa].push_back(v); // 上放到其父节点中}w[i].pop_back();}
}
int main() {cin>>n;for(int i = 1 ; i <= n + n ; i ++) cin>>s[i];for(int i = n + 1 ; i <= n + n ; i ++) insert(s[i] , i - n); // 把笔名插入字典树for(int i = 1 ; i <= n ; i ++) find(s[i] , i); // 找每个真名与所有笔名中的最长公共前缀 dfs(0 , 0 , 0 , 0);cout<<h<<'\n';for(int i = 1 ; i <= n ; i ++) {cout<<i<<' '<<ans[i]<<'\n';}return 0;
}
http://www.wxhsa.cn/company.asp?id=3150

相关文章:

  • Tarjan 求连通性相关
  • 暑假学习笔记
  • qoj #8557. Goldberg Machine 题解
  • centos7云主机磁盘清理过程纪要
  • 『随笔』我的唱歌练习史
  • 2025浙江省信息通信业职业技能竞赛-数据安全管理员竞赛-决赛wp
  • Java基础核心问题解析
  • 2025年浙江省信息通信业职业技能竞赛-数据安全管理员竞赛-初赛WriteUp
  • 九三阅兵实时记录+次日补
  • 铸网-2025”山东省工业互联网网络安全职业技能竞赛wp(职工组)
  • 视洞R33定制版改造自制IPC网络摄像头(可rtsp可web)
  • 二十一、流水线的冒险与处理
  • java线程的一些思考
  • 2025智能数据分类分级产品选型指南:构建数据治理的智能基座
  • 这是我的第一个博客
  • eqw
  • 2.第一个c语言项目
  • GitHub Copilot 2025年8月最新更新!
  • NOIP 模拟赛十五
  • 面试必备进程调度:fg,bg,jobs,ctrl+z,
  • 完整教程:计算机毕设 java 多媒体教室管理系统 基于 Java+SSM 的多媒体教室运维平台 Java+MySQL 的教室预约与设备管理系统
  • 笔记一
  • 二十、指令流水线的基本实现
  • 物料模板匹配成功后,自动跟随的逻辑
  • TCL t508n 关闭电话语音王提醒/改用4G
  • 完整教程:Markdown 编辑器 语法
  • 天地图的带洞多边形操作
  • k8s集群中一台etcd的pod异常
  • 深入解析:基于51单片机电子称称重压力检测阈值报警系统设计
  • 手撕大模型|KVCache 原理及代码解析