字符串的幂次分割问题
字数 2911 2025-12-08 00:56:59

字符串的幂次分割问题

给定一个字符串 s 和一个整数 k。我们可以将 s 分割成若干非空子串,如果某个子串 t 满足:

  • t 可以由它的某个真前缀重复多次得到(即 t 是该真前缀的幂次,例如 "abab" 可由 "ab" 重复 2 次得到,"abc" 不是任何真前缀的幂次,因为其最小重复单元是自身,但真前缀不能是自身)。

但本题的规则略有特殊:

  • 只有当子串 t 的长度能整除 k 时,才允许将其作为一个分割单元。
  • 将字符串分割成若干这样的子串后,每个子串都会有一个得分:
    • 如果子串 t 是幂次串(由长度小于 t 的真前缀重复构成),得分为 a(给定正整数)。
    • 否则,得分为 b(给定负整数,即惩罚)。
  • 总得分为各子串得分之和。
  • 可以任意分割,也可以不分割(即整个字符串作为一个子串处理),但必须保证每个子串的长度是 k 的整数倍。

最大可能得分


解题思路

这个问题是区间动态规划的一个变体。
我们需要考虑两点:

  1. 如何判断一个子串是否为“幂次串”(即由真前缀重复构成)。
  2. 如何在满足“长度是 k 的整数倍”的条件下,进行最优分割。

定义

  • 子串 s[i:j] 表示从索引 ij-1(左闭右开,长度 len = j-i)。
  • 幂次串条件:假设子串长度为 L,如果存在整数 m > 1 和长度 d 使得 L = m * d,且 s[i:i+L]s[i:i+d] 重复 m 次构成,则它是幂次串。注意,d 必须整除 L,且 d < L(真前缀)。

步骤 1:判断幂次串的快速方法

对于一个子串 s[l:r](长度 L = r-l+1),我们需要检查是否存在长度 d 满足:

  • d 整除 Ld < L
  • 对任意 t0L-1,有 s[l + t] == s[l + (t % d)]

这可以在 O(L × 约数个数) 内完成,但为了优化,我们可以用 KMP 的“最小周期”方法:

  • 对于长度为 L 的字符串,其最小周期 p 满足 L % p == 0p 是最小的。
  • 如果 p < L,则说明字符串是周期串,由长度为 p 的前缀重复 L/p 次构成。

因此我们可以提前预处理一个二维数组 period[l][r] 表示子串 s[l:r+1] 的最小周期长度。
计算 period[l][r] 可以用 KMP 前缀函数在 O(n³) 内完成(n 是字符串长度),但更简单的方法是暴力检查约数,因为 n ≤ 200 时是可接受的。


步骤 2:动态规划状态定义

定义 dp[i] 表示从前 i 个字符(即 s[0:i])中获得的最大得分。
注意:dp[0] = 0(空串得分为 0)。

转移时,我们需要考虑最后一个子串的结束位置是 i,起始位置是 j0 ≤ j < i),且子串长度 (i-j) 必须能被 k 整除。

状态转移方程:

dp[i] = max(dp[i], dp[j] + score(j, i-1))

其中 score(l, r) 表示子串 s[l:r+1] 的得分:

  • 如果它是幂次串(即 period[l][r] < (r-l+1)),得分为 a
  • 否则,得分为 b

但这里有一个关键:整个子串长度必须整除 k,所以如果 (r-l+1) % k != 0,则该子串不能作为分割单元,所以 score(l, r) 在这种无效情况下应设为负无穷(即不允许)。


步骤 3:计算 period 数组

我们可以在 O(n³) 内暴力计算:
对每个 (l, r),长度 len = r-l+1,枚举所有整除 len 的数 dd < len),检查是否满足周期性。
检查方法:看从 l+dr 的每个字符是否等于 l + ( (pos-l) % d ) 位置的字符。

若找到最小的 d 满足周期条件,则 period[l][r] = d,否则 period[l][r] = len(即最小周期是自身)。


步骤 4:动态规划实现顺序

我们按 i 从 1 到 n 遍历:

  • 对每个 i,遍历 j 从 0 到 i-1,如果 (i-j) % k == 0,则计算得分并更新 dp[i]
    初始化 dp[0]=0dp[其它]=-∞(因为可能有负分,所以初始化为负无穷,表示不可达)。

最终答案 = dp[n]


步骤 5:示例演算

假设:
s = "ababab", k=2, a=5, b=-2

字符串长度 n=6。

预处理 period 矩阵(只列举重要部分):

  • "ab" (l=0,r=1, len=2):周期是自身 2(因为 d=1 不行,'a'≠'b'),所以不是幂次串。
  • "abab" (l=0,r=3, len=4):d=2 时,s[0]='a',s[2]='a',s[1]='b',s[3]='b',满足,周期=2<4,是幂次串。
  • "ababab" (l=0,r=5, len=6):d=2 时,检查 "ab" 重复 3 次,满足,周期=2<6,是幂次串。

动态规划
dp[0]=0

i=2(考虑前 2 个字符 "ab"):
j=0,长度=2 整除 k=2,"ab" 不是幂次串,得分=-2,dp[2]=dp[0]+(-2)=-2

i=4(前 4 个字符):

  • 整体不分:j=0,长度=4 整除 2,"abab" 是幂次串,得分=5,dp[4]=dp[0]+5=5
  • 分割成 "ab"+"ab":先到 j=2,长度=2,"ab" 得分=-2,dp[4] = max(dp[4], dp[2]+(-2)) = max(5, -2-2=-4) = 5

i=6:

  • 整体不分:j=0,长度=6 整除 2,"ababab" 是幂次串,得分=5,dp[6]=5
  • 分割成 "abab"+"ab":j=4,长度=2,"ab" 得分=-2,dp[6]=max(5, dp[4]+(-2)=5-2=3)=5
  • 分割成 "ab"+"abab":j=2,长度=4,"abab" 得分=5,dp[6]=max(5, dp[2]+5= -2+5=3)=5
  • 分割成 "ab"+"ab"+"ab"
    这对应 j=2 和 j=4 的两步,但我们可以从 j=4 得到:dp[4]+score(4,5)score(4,5)="ab" 得分=-2,dp[6]=max(5, dp[4]-2=5-2=3)=5

最终 dp[6]=5


步骤 6:算法复杂度

  • 预处理 period:O(n³),但可以优化到 O(n²) 用 KMP,这里用暴力 O(n³) 在 n≤200 可接受。
  • DP:O(n²)。

总 O(n³) 可接受。

字符串的幂次分割问题 给定一个字符串 s 和一个整数 k 。我们可以将 s 分割成若干 非空 子串,如果某个子串 t 满足: t 可以由它的某个 真前缀 重复多次得到(即 t 是该真前缀的幂次,例如 "abab" 可由 "ab" 重复 2 次得到, "abc" 不是任何真前缀的幂次,因为其最小重复单元是自身,但真前缀不能是自身)。 但本题的规则略有特殊: 只有当子串 t 的长度能 整除 k 时,才允许将其作为一个分割单元。 将字符串分割成若干这样的子串后,每个子串都会有一个得分: 如果子串 t 是幂次串(由长度小于 t 的真前缀重复构成),得分为 a (给定正整数)。 否则,得分为 b (给定负整数,即惩罚)。 总得分为各子串得分之和。 可以任意分割,也可以不分割(即整个字符串作为一个子串处理),但必须保证每个子串的长度是 k 的整数倍。 求 最大可能得分 。 解题思路 这个问题是区间动态规划的一个变体。 我们需要考虑两点: 如何判断一个子串是否为“幂次串”(即由真前缀重复构成)。 如何在满足“长度是 k 的整数倍”的条件下,进行最优分割。 定义 : 子串 s[i:j] 表示从索引 i 到 j-1 (左闭右开,长度 len = j-i )。 幂次串条件:假设子串长度为 L ,如果存在整数 m > 1 和长度 d 使得 L = m * d ,且 s[i:i+L] 由 s[i:i+d] 重复 m 次构成,则它是幂次串。注意, d 必须整除 L ,且 d < L (真前缀)。 步骤 1:判断幂次串的快速方法 对于一个子串 s[l:r] (长度 L = r-l+1 ),我们需要检查是否存在长度 d 满足: d 整除 L 且 d < L 。 对任意 t 从 0 到 L-1 ,有 s[l + t] == s[l + (t % d)] 。 这可以在 O(L × 约数个数) 内完成,但为了优化,我们可以用 KMP 的“最小周期”方法: 对于长度为 L 的字符串,其最小周期 p 满足 L % p == 0 且 p 是最小的。 如果 p < L ,则说明字符串是周期串,由长度为 p 的前缀重复 L/p 次构成。 因此我们可以提前预处理一个二维数组 period[l][r] 表示子串 s[l:r+1] 的最小周期长度。 计算 period[l][r] 可以用 KMP 前缀函数在 O(n³) 内完成(n 是字符串长度),但更简单的方法是暴力检查约数,因为 n ≤ 200 时是可接受的。 步骤 2:动态规划状态定义 定义 dp[i] 表示 从前 i 个字符 (即 s[0:i] )中获得的最大得分。 注意: dp[0] = 0 (空串得分为 0)。 转移时,我们需要考虑最后一个子串的结束位置是 i ,起始位置是 j ( 0 ≤ j < i ),且子串长度 (i-j) 必须能被 k 整除。 状态转移方程: 其中 score(l, r) 表示子串 s[l:r+1] 的得分: 如果它是幂次串(即 period[l][r] < (r-l+1) ),得分为 a 。 否则,得分为 b 。 但这里有一个关键: 整个子串长度必须整除 k ,所以如果 (r-l+1) % k != 0 ,则该子串不能作为分割单元,所以 score(l, r) 在这种无效情况下应设为负无穷(即不允许)。 步骤 3:计算 period 数组 我们可以在 O(n³) 内暴力计算: 对每个 (l, r) ,长度 len = r-l+1 ,枚举所有整除 len 的数 d ( d < len ),检查是否满足周期性。 检查方法:看从 l+d 到 r 的每个字符是否等于 l + ( (pos-l) % d ) 位置的字符。 若找到最小的 d 满足周期条件,则 period[l][r] = d ,否则 period[l][r] = len (即最小周期是自身)。 步骤 4:动态规划实现顺序 我们按 i 从 1 到 n 遍历: 对每个 i ,遍历 j 从 0 到 i-1,如果 (i-j) % k == 0 ,则计算得分并更新 dp[i] 。 初始化 dp[0]=0 , dp[其它]=-∞ (因为可能有负分,所以初始化为负无穷,表示不可达)。 最终答案 = dp[n] 。 步骤 5:示例演算 假设: s = "ababab" , k=2 , a=5 , b=-2 。 字符串长度 n=6。 预处理 period 矩阵 (只列举重要部分): "ab" ( l=0,r=1 , len=2):周期是自身 2(因为 d=1 不行,'a'≠'b'),所以不是幂次串。 "abab" ( l=0,r=3 , len=4):d=2 时,s[ 0]='a',s[ 2]='a',s[ 1]='b',s[ 3]='b',满足,周期=2 <4,是幂次串。 "ababab" ( l=0,r=5 , len=6):d=2 时,检查 "ab" 重复 3 次,满足,周期=2 <6,是幂次串。 动态规划 : dp[0]=0 i=2(考虑前 2 个字符 "ab" ): j=0,长度=2 整除 k=2, "ab" 不是幂次串,得分=-2, dp[2]=dp[0]+(-2)=-2 i=4(前 4 个字符): 整体不分:j=0,长度=4 整除 2, "abab" 是幂次串,得分=5, dp[4]=dp[0]+5=5 分割成 "ab"+"ab" :先到 j=2,长度=2, "ab" 得分=-2, dp[4] = max(dp[4], dp[2]+(-2)) = max(5, -2-2=-4) = 5 i=6: 整体不分:j=0,长度=6 整除 2, "ababab" 是幂次串,得分=5, dp[6]=5 分割成 "abab"+"ab" :j=4,长度=2, "ab" 得分=-2, dp[6]=max(5, dp[4]+(-2)=5-2=3)=5 分割成 "ab"+"abab" :j=2,长度=4, "abab" 得分=5, dp[6]=max(5, dp[2]+5= -2+5=3)=5 分割成 "ab"+"ab"+"ab" : 这对应 j=2 和 j=4 的两步,但我们可以从 j=4 得到: dp[4]+score(4,5) , score(4,5) = "ab" 得分=-2, dp[6]=max(5, dp[4]-2=5-2=3)=5 最终 dp[6]=5 。 步骤 6:算法复杂度 预处理 period:O(n³),但可以优化到 O(n²) 用 KMP,这里用暴力 O(n³) 在 n≤200 可接受。 DP:O(n²)。 总 O(n³) 可接受。