1 条题解

  • 0
    @ 2025-10-31 19:19:17

    题目理解

    我们要求的是:对于字符串 SS每一个子串,统计它有多少种优秀的拆分,然后把所有子串的优秀拆分个数加起来。

    一个优秀的拆分是:子串可以写成 AABB\text{AABB} 形式,其中 A\text{A}B\text{B} 非空。


    思路分析

    1. 暴力方法

    枚举所有子串 S[i..j]S[i..j],再枚举拆分点,判断是否满足 AABB\text{AABB} 形式。
    复杂度 O(n4)O(n^4)O(n3)O(n^3),显然不可行。

    2. 转化问题

    AABB\text{AABB} 可以看作两个 AA\text{AA} 形式的串拼接而成,中间切一刀:
    设切点在 ppp+1p+1 之间,那么前半部分 S[1..p]S[1..p] 的某个后缀是 AA\text{AA},后半部分 S[p+1..n]S[p+1..n] 的某个前缀是 BB\text{BB}

    更精确地,对于一个固定的切分位置 pp(即 AABB\text{AABB} 中第一个 B\text{B} 开始的位置的前一个位置),我们要数有多少对 (A,B)(A,B) 使得:

    • S[p2A+1..p]=AAS[p-2|A|+1 .. p] = \text{AA}
    • S[p+1..p+2B]=BBS[p+1 .. p+2|B|] = \text{BB}

    这样还是复杂,我们换一种思路。


    3. 关键观察

    AABB\text{AABB} 可以看作在某个位置 ii 切分,左边是 AA\text{AA} 的结束位置,右边是 BB\text{BB} 的开始位置。

    具体来说,设 pp 是第一个 B\text{B} 的开头位置的前一个位置(即 AA\text{AA} 的结尾位置),那么:

    • pp 处结束的形如 XX\text{XX} 的串的个数,记作 f(p)f(p)
    • p+1p+1 处开始的形如 XX\text{XX} 的串的个数,记作 g(p+1)g(p+1)

    那么以 ppAA\text{AA}BB\text{BB} 的分界点,优秀拆分的个数就是 f(p)×g(p+1)f(p) \times g(p+1)

    因此,问题转化为:

    1. 对每个位置 ii,计算有多少个 AA\text{AA}ii 结尾 → f(i)f(i)
    2. 对每个位置 ii,计算有多少个 AA\text{AA}ii 开头 → g(i)g(i)
    3. 答案 =p=1n1f(p)g(p+1)= \sum_{p=1}^{n-1} f(p) \cdot g(p+1)

    4. 如何求 f(i)f(i)g(i)g(i)

    求所有 AA\text{AA} 型子串的出现位置,是一个经典问题。

    方法: 使用后缀数组 (Suffix Array) + 高度数组 (LCP)哈希 + 枚举长度

    枚举长度法

    枚举 AA\text{AA} 的长度 lenlen,那么 AA\text{AA} 由两个相同子串拼接,中间切点在某个位置。

    我们枚举 lenlen,然后在字符串上以步长 lenlen 设置关键点,相邻关键点求最长公共前缀 (LCP) 和最长公共后缀 (LCS),就可以得到 AA\text{AA} 出现的区间。

    具体做法(常见套路):

    • 设关键点为 klenk \cdot len(k+1)len(k+1)\cdot len
    • 计算 LCP(s[klen..n],s[(k+1)len..n])LCP(s[k\cdot len .. n], s[(k+1)\cdot len .. n])LCS(s[1..klen],s[1..(k+1)len])LCS(s[1 .. k\cdot len], s[1 .. (k+1)\cdot len])
    • 通过 LCP 和 LCS 可以确定 AA\text{AA} 覆盖的区间,然后用差分数组标记这些 AA\text{AA} 的起始和结束位置。

    这样我们就能得到:

    • ii 结尾的 AA\text{AA} 个数 f(i)f(i)
    • ii 开头的 AA\text{AA} 个数 g(i)g(i)

    5. 复杂度

    枚举 lenlen11nn,内部 O(n/len)O(n/len) 个关键点,每个关键点求 LCP/LCS 如果用哈希+二分是 O(logn)O(\log n),总复杂度 O(nlog2n)O(n \log^2 n),可过 n30000n \le 30000

    如果用后缀数组 + ST 表求 LCP,可以做到 O(nlogn)O(n \log n)


    6. 算法步骤

    1. 读入多组数据。
    2. 对每组数据:
      • 初始化 f[1..n]=0f[1..n] = 0, g[1..n]=0g[1..n] = 0
      • 枚举 len=1len = 1n/2n/2
        • k=0,1,k = 0,1,\dots(k+1)lenn(k+1)len \le n
          • 计算 LCP(klen+1,(k+1)len+1)LCP(k\cdot len+1, (k+1)\cdot len+1)
          • 计算 LCS(klen,(k+1)len)LCS(k\cdot len, (k+1)\cdot len)
          • 确定 AA\text{AA} 出现的区间,更新 ffgg 的差分数组
      • 对差分数组求前缀和得到真正的 ffgg
      • 计算 p=1n1f(p)g(p+1)\sum_{p=1}^{n-1} f(p) \cdot g(p+1),输出答案
    • 1

    信息

    ID
    4849
    时间
    1500ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    1
    已通过
    1
    上传者