字符串的幂次分割问题
给定一个字符串 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 整除。
状态转移方程:
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 的数 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³) 可接受。