关于CF-858D的思路和题解
题意:n 个 9 位电话号码。对每个号码,找最短连续子串,满足:只有当前这个号码包含该子串,别的号码都没有。
核心思想
任意字符串的任意子串,一定是它某个后缀的前缀。
例:串 100123456,子串 01,它是后缀 0123456 的前缀。
那我们把每个号码全部后缀插到字典树。字典树上一条路径,就对应一个子串。
字典树节点存两件东西:
- son [10]:10 个数字子节点
- cnt:有多少不同号码含有这个子串
坑:同一个号码里面,同一个子串可能出现多次。但是 cnt 只能 + 1,不能反复加。
原来写法用大 vis 数组,每次插入memset清空整个数组,数组 3e6 大小,7e4 次插入直接超时。
优化:时间戳(代替 vis 数组)
开数组 tim [],再加变量 now。
- 每处理一个新号码,now += 1
- 访问节点 u:如果
tim[u] != now,说明本轮这个号码第一次走到这个节点- cnt ++
- 把
tim[u] = now打上标记
- 全程不用 memset!不用清空数组,这就是解决 TLE 的关键。
插入流程 ins (s)
- now++,开启当前号码的标记轮次
- st 从 0 到 8,枚举所有后缀起点
- u=0(根节点),从 st 往后遍历每一位数字
- 没有子节点就新建节点,idx++
- 跳到子节点 u
- 判断 tim [u] 是否不等于 now:
- 是的 → cnt+1,tim [u]=now,标记这个号码已经算过这个节点
- 不是 → 跳过,不再增加 cnt
查询找答案 findans (s)
长度从小到大试,保证找到最短
len=1 先试,再 len=2,直到 9:
对每个起点 st,取长度 len 的子串
在字典树上顺着数字走 len 步
走通 且 当前节点 cnt ==1 → 这个子串只属于当前号码,直接返回。
主函数
读 n,读全部号码,逐个插入字典树。
再遍历每个号码,调用 findans,输出答案。
代码如下:


浙公网安备 33010602011771号