区间动态规划例题:统计同构子字符串的数目问题(字符替换成本版本)
字数 1237 2025-11-13 22:01:11
区间动态规划例题:统计同构子字符串的数目问题(字符替换成本版本)
题目描述
给定一个字符串s和一个整数数组cost,其中cost[i]表示将字符s[i]替换为任意其他字符的成本。我们可以执行任意次替换操作,每次操作的成本为cost[i]。
定义同构子字符串:如果一个字符串的所有字符都相同,则称该字符串为同构字符串。
请计算通过执行替换操作(总成本不超过预算B)后,字符串s中可能的最长同构子字符串的长度。
解题过程
这个问题结合了区间动态规划和二分查找的思想。我们需要找到在成本限制下能够获得的最长同构子串。
步骤1:问题分析
- 目标:在预算B内,通过替换某些字符,使得字符串中出现尽可能长的连续相同字符段
- 约束:总替换成本不能超过B
- 关键观察:最优解一定是将某个区间内的字符全部替换为同一个字符
步骤2:动态规划状态定义
定义dp[i][j]表示将子串s[i...j]变为同构字符串的最小成本。
由于我们需要处理任意字符替换,状态需要记录目标字符:
dp[i][j][c] = 将s[i...j]全部变为字符c的最小成本
但这样状态空间太大(O(n²×26)),需要寻找更高效的方法。
步骤3:优化状态设计
观察发现,对于任意区间[i,j],最优解一定是全部变为某个出现在该区间中的字符(因为如果目标字符不在区间中出现,成本可能更高)。
我们可以定义:
dp[i][j] = 将s[i...j]变为同构字符串的最小成本
状态转移:
- 如果s[i] == s[j],那么dp[i][j] = dp[i+1][j-1] + 替换中间字符的成本
- 否则,dp[i][j] = min(dp[i+1][j] + cost[i], dp[i][j-1] + cost[j])
步骤4:更优的解法——滑动窗口+前缀和
实际上,这个问题有更高效的O(n)解法:
对于每个可能的字符c,我们计算将某个区间内所有非c字符替换为c的成本,找到在预算B内的最长区间。
具体算法:
- 对于每个字符c(26种可能)
- 使用滑动窗口,维护窗口内将非c字符替换为c的总成本
- 当成本超过B时,移动左指针
- 记录最大窗口长度
步骤5:滑动窗口实现细节
def longestHomogeneousSubstring(s: str, cost: List[int], B: int) -> int:
max_len = 0
n = len(s)
for target_char in set(s): # 遍历所有可能的目标字符
left = 0
current_cost = 0
for right in range(n):
# 如果当前字符不是目标字符,需要替换
if s[right] != target_char:
current_cost += cost[right]
# 如果成本超预算,移动左指针
while current_cost > B and left <= right:
if s[left] != target_char:
current_cost -= cost[left]
left += 1
# 更新最大长度
max_len = max(max_len, right - left + 1)
return max_len
步骤6:复杂度分析
- 时间复杂度:O(26×n) = O(n),其中26是字符集大小
- 空间复杂度:O(1),只使用了常数个变量
步骤7:边界情况处理
- 空字符串:返回0
- 预算B=0:只能使用原本就是同构的子串
- 所有cost[i]=0:可以任意替换,答案为整个字符串长度
步骤8:示例验证
例:s = "aabaa", cost = [1,1,3,2,1], B = 2
- 目标字符'a':最长有效区间[0,4],成本=3(替换索引2的'b'),但成本超预算
- 调整后得到区间[0,3],长度=4
- 最终答案:4
这种方法确保了在O(n)时间内找到最优解,比传统的区间DP更高效。