关于CF-858D的思路和题解

题意:n 个 9 位电话号码。对每个号码,找最短连续子串,满足:只有当前这个号码包含该子串,别的号码都没有。

核心思想

任意字符串的任意子串,一定是它某个后缀的前缀。
例:串 100123456,子串 01,它是后缀 0123456 的前缀。
那我们把每个号码全部后缀插到字典树。字典树上一条路径,就对应一个子串。

字典树节点存两件东西:

  1. son [10]:10 个数字子节点
  2. cnt:有多少不同号码含有这个子串

坑:同一个号码里面,同一个子串可能出现多次。但是 cnt 只能 + 1,不能反复加。
原来写法用大 vis 数组,每次插入memset清空整个数组,数组 3e6 大小,7e4 次插入直接超时。

优化:时间戳(代替 vis 数组)

开数组 tim [],再加变量 now。

  • 每处理一个新号码,now += 1
  • 访问节点 u:如果tim[u] != now,说明本轮这个号码第一次走到这个节点
    • cnt ++
    • 把tim[u] = now打上标记
  • 全程不用 memset!不用清空数组,这就是解决 TLE 的关键。

插入流程 ins (s)

  1. now++,开启当前号码的标记轮次
  2. st 从 0 到 8,枚举所有后缀起点
  3. u=0(根节点),从 st 往后遍历每一位数字
  4. 没有子节点就新建节点,idx++
  5. 跳到子节点 u
  6. 判断 tim [u] 是否不等于 now:
    • 是的 → cnt+1,tim [u]=now,标记这个号码已经算过这个节点
    • 不是 → 跳过,不再增加 cnt

查询找答案 findans (s)

长度从小到大试,保证找到最短
len=1 先试,再 len=2,直到 9:
对每个起点 st,取长度 len 的子串
在字典树上顺着数字走 len 步
走通 且 当前节点 cnt ==1 → 这个子串只属于当前号码,直接返回。

主函数

读 n,读全部号码,逐个插入字典树。
再遍历每个号码,调用 findans,输出答案。
代码如下:
image
image

posted @ 2026-10-20 15:45  事事随风散  阅读(4)  评论(0)    收藏  举报