1 条题解

  • 0
    @ 2025-10-17 12:58:37

    题目大意

    我们有两个命名串:

    • 去年的命名串是 SS 的某个子串 S[l..r]S[l..r]SS 是已知的固定串)
    • 今年的命名串是 TT(每次询问可能不同)

    题目要求:找出所有满足以下条件的命名方案数:

    1. 是今年命名串 TT非空连续子串
    2. 一定不会出现在去年命名串 S[l..r]S[l..r]

    核心思路分析

    第一步:转化问题

    设:

    • AA = TT 的所有不同非空子串个数
    • BB = 同时出现在 TTS[l..r]S[l..r] 中的不同子串个数

    那么答案就是:

    答案=AB\text{答案} = A - B

    因为我们要找的是在 TT 中但不在 S[l..r]S[l..r] 中的子串。


    第二步:计算 AA

    TT 的所有不同非空子串个数可以用后缀自动机(SAM)轻松求出。

    对于字符串 TT,建立其 SAM,那么:

    $$A = \sum_{u \in \text{SAM}(T)} (\text{len}(u) - \text{len}(\text{link}(u))) $$

    其中 len(u)\text{len}(u) 表示状态 uu 能接受的最长子串长度。


    第三步:计算 BB

    这是本题的难点:求两个字符串的公共子串个数,但其中一个字符串是固定长串 SS 的子区间。

    3.1 对 SS 建立后缀自动机

    由于所有询问的 S[l..r]S[l..r] 都来自同一个母串 SS,我们可以预先为整个 SS 建立 SAM。

    3.2 在 SS 的 SAM 上匹配 TT

    我们让 TTSS 的 SAM 上运行,同时记录:

    • 当前匹配长度 cur_len
    • 当前状态 state

    对于 TT 的每个前缀 T[1..i]T[1..i],我们想知道它与 S[l..r]S[l..r] 的最长公共后缀长度。

    3.3 处理区间限制 [l,r][l, r]

    关键问题:SS 的 SAM 接受的是整个 SS,但我们需要只考虑 S[l..r]S[l..r] 的子串。

    解决方案:使用线段树合并预处理每个 SAM 状态的 endpos 集合,然后检查当前状态是否能出现在区间 [l,r][l, r] 中。

    具体步骤:

    1. SS 的 SAM 的每个状态,用线段树合并记录其所有 endpos
    2. 匹配 TT 时,维护 (state, cur_len) 表示当前匹配状态和长度
    3. 如果当前状态的 endpos[l,r][l, r] 中有出现,说明匹配成功
    4. 否则,沿着后缀链接 link 回退,直到找到在 [l,r][l, r] 中有出现的状态

    3.4 计算公共子串个数

    对于 TT 的每个位置 ii,我们得到以 ii 结尾的与 S[l..r]S[l..r] 的最长公共子串长度 LiL_i

    那么所有公共子串个数为:

    B=i=1TLiB = \sum_{i=1}^{|T|} L_i

    因为对于每个结束位置 ii,长度 11LiL_i 的子串都是公共的,且不会重复计数(这些子串的结束位置不同)。


    第四步:算法流程

    1. 预处理

      • 对母串 SS 建立后缀自动机
      • 对 SAM 进行线段树合并,记录每个状态的 endpos 集合
    2. 处理每个询问

      • 读取 T,l,rT, l, r
      • 计算 AA = TT 的不同子串数(用 TT 的 SAM)
      • 计算 BB = TTS[l..r]S[l..r] 的公共子串数:
        • 初始化 state = root, cur_len = 0, B = 0
        • 对于 i=1i = 1T|T|
          • 沿着 SAM 的转移边前进,同时保持 state[l,r][l, r] 区间内有效
          • 更新 cur_lenstate
          • 如果无法前进,沿后缀链接回退直到可以继续
          • LiL_i = 当前的 cur_len
          • BB += LiL_i
      • 输出 ABA - B

    时间复杂度

    • 预处理 SS 的 SAM 和线段树合并:O(SlogS)O(|S| \log |S|)
    • 每个询问:O(TlogS)O(|T| \log |S|)
    • 总复杂度:O(SlogS+TlogS)O(|S| \log |S| + \sum |T| \log |S|)

    样例解析

    以第一个询问为例:

    S = "scbamgepe"
    T = "smape", l=2, r=7 → S[2..7] = "cbamge"
    
    • TT 的所有不同子串:很多,计算得 A=15A = 15
    • TT 与 "cbamge" 的公共子串:"m", "a", "p", "ma" 等,计算得 B=3B = 3
    • 答案 = 153=1215 - 3 = 12

    代码实现要点

    1. 使用结构体实现 SAM,支持动态添加字符
    2. 线段树合并时使用动态开点
    3. 匹配时注意处理回退情况
    4. 注意数组大小,避免 MLE

    总结

    本题综合运用了:

    • 后缀自动机(SAM)
    • 线段树合并
    • 区间子串查询
    • 双字符串匹配

    关键突破点是将"与区间子串的公共子串计数"转化为在全局 SAM 上的匹配问题,通过线段树合并解决区间限制。

    • 1

    信息

    ID
    3217
    时间
    3000ms
    内存
    1024MiB
    难度
    10
    标签
    递交数
    1
    已通过
    1
    上传者