合并相邻同色字符的最小操作次数问题(每次合并两个相邻同色字符,合并产生新字符,新字符颜色可不同,每次合并有操作成本)
题目描述
给定一个由 n 个字符组成的字符串 s,每个字符都有一个颜色(用一个小写字母表示,颜色种类有限,例如 'a' 到 'z')。你有一系列操作:每次操作可以选择两个相邻且颜色相同的字符,将它们合并成一个新的字符。新字符的颜色可以是任意颜色(不一定要与原来的颜色相同),并且这次操作会有一个成本,成本取决于被合并的两个字符的颜色以及合并后产生的新字符的颜色。具体来说,给你一个三维数组 cost[old1][old2][new],表示将两个颜色分别为 old1 和 old2(这里因为要求相邻且同色,所以 old1 == old2)的字符合并成颜色为 new 的字符所需的成本。题目保证 cost[a][a][b] 总是非负整数。你的目标是通过一系列这样的合并操作,最终将整个字符串合并成一个字符,求所需的最小总成本。如果无法合并成一个字符,则返回 -1。
示例:
假设字符串 s = "aa",颜色只有 'a' 和 'b',成本数组为:
- cost['a']['a']['a'] = 1
- cost['a']['a']['b'] = 3
那么你可以:
- 将两个 'a' 合并成 'a',成本 = 1,得到字符串 "a"(已合并成一个字符),总成本 = 1。
- 将两个 'a' 合并成 'b',成本 = 3,得到字符串 "b",总成本 = 3。
所以最小成本为 1。
解题过程循序渐进讲解
步骤1:问题分析与建模
这个问题是一个典型的区间动态规划问题,因为合并操作只允许相邻字符进行,并且最终目标是将整个区间(字符串)合并成一个字符。关键点在于:
- 每次合并后,原来的两个字符消失,新字符出现在它们的位置,长度减1。
- 新字符的颜色可以是任意的,这会影响后续合并的成本。
- 我们需要记录区间合并后可能得到的颜色,并计算对应的最小成本。
步骤2:定义状态
定义 dp[i][j][c] 表示将子字符串 s[i...j](闭区间)合并成一个颜色为 c 的字符所需的最小成本。如果无法合并成颜色 c,则值为无穷大(用一个大数表示)。
i和j是字符串的索引,满足0 <= i <= j < n。c是颜色,假设颜色总数为m(例如m=26表示小写字母)。
最终答案:对于所有可能的颜色 c,dp[0][n-1][c] 的最小值。如果所有值都是无穷大,则返回 -1。
步骤3:状态转移方程
考虑如何计算 dp[i][j][c]:
-
基本情况:当
i == j时,区间只有一个字符。如果这个字符的颜色就是 c,那么不需要任何合并,成本为 0;否则,不可能直接得到颜色 c,成本为无穷大。dp[i][i][c] = 0 if s[i] == c else INF
-
一般情况:
i < j。我们需要将区间[i, j]合并成一个颜色为 c 的字符。最后一次操作一定是将某两个相邻且颜色相同的字符合并成颜色 c。但在此之前,区间可能已经被合并成两个字符,或者更多?实际上,因为每次合并减少一个字符,所以最终合并成 c 的操作,其前一步应该有两个子区间各自合并成一个字符,且这两个字符颜色相同(设为 k),然后将它们合并成 c。因此,我们可以在区间
[i, j]中找一个分割点mid(i <= mid < j),使得:- 左区间
[i, mid]合并成一个颜色为 k 的字符,最小成本为dp[i][mid][k]。 - 右区间
[mid+1, j]合并成一个颜色也为 k 的字符,最小成本为dp[mid+1][j][k]。 - 然后将这两个颜色为 k 的字符合并成颜色 c,成本为
cost[k][k][c]。
由于 k 可以是任意颜色,我们需要枚举所有可能的 k,并取最小成本:
dp[i][j][c] = min_{k in colors} ( dp[i][mid][k] + dp[mid+1][j][k] + cost[k][k][c] )但注意:分割点
mid也需要枚举,因为合并成两个颜色为 k 的字符的分割位置可能不同。所以完整转移为:dp[i][j][c] = min_{mid from i to j-1} min_{k in colors} ( dp[i][mid][k] + dp[mid+1][j][k] + cost[k][k][c] ) - 左区间
步骤4:计算顺序
由于 dp[i][j] 依赖于更小的区间([i, mid] 和 [mid+1, j] 长度更小),所以我们按照区间长度从小到大的顺序计算:
- 外层循环:长度
len从 1 到 n(长度为 1 即基本情况)。 - 内层循环:左端点
i从 0 到n-len,则j = i + len - 1。 - 对于每个
(i, j),枚举颜色 c,枚举分割点 mid,枚举中间颜色 k,更新dp[i][j][c]。
步骤5:初始化与边界
- 初始化
dp数组为无穷大(例如INF = 10**9)。 - 对于长度为 1 的区间:
dp[i][i][s[i]] = 0,其余颜色为 INF。 - 注意:如果
cost[k][k][c]在某些情况下不存在(例如颜色编号超出范围),应视为 INF。
步骤6:最终答案
计算完所有状态后,答案 = min_{c in colors} dp[0][n-1][c]。如果这个最小值是 INF,说明无法合并成一个字符,返回 -1;否则返回该最小值。
步骤7:时间复杂度
- 状态数:
O(n^2 * m),其中 m 是颜色数量。 - 转移:枚举分割点 O(n),枚举中间颜色 k O(m),所以总转移复杂度 O(n * m)。
- 总复杂度:
O(n^3 * m^2)。如果 m 较小(例如 26),n 不超过 100,则可以在合理时间内完成。
步骤8:示例推导
以 s="aba",颜色集为 {'a','b'} 为例,假设成本如下:
- cost[a][a][a]=0, cost[a][a][b]=1
- cost[b][b][a]=2, cost[b][b][b]=0
(其他组合如 cost[a][b][*] 不会用到,因为只合并同色字符)
计算过程:
-
初始化长度为1:
- dp[0][0]['a']=0, dp[0][0]['b']=INF
- dp[1][1]['b']=0, dp[1][1]['a']=INF
- dp[2][2]['a']=0, dp[2][2]['b']=INF
-
长度=2,区间 [0,1]("ab"):
- 无法合并,因为两个字符颜色不同,所以 dp[0][1][a]=INF, dp[0][1][b]=INF。
区间 [1,2]("ba")同样全部 INF。
- 无法合并,因为两个字符颜色不同,所以 dp[0][1][a]=INF, dp[0][1][b]=INF。
-
长度=3,区间 [0,2]("aba"):
枚举 c='a',枚举 mid=0 或 1。- mid=0:左 [0,0]('a'),右 [1,2]("ba")。右区间合并成一个颜色 k 的最小成本:dp[1][2][a]=INF, dp[1][2][b]=INF,所以不可行。
- mid=1:左 [0,1]("ab"),右 [2,2]('a')。左区间全部 INF,不可行。
因此 dp[0][2][a]=INF。
枚举 c='b': - mid=0:左 [0,0]('a')成本0,右 [1,2]("ba")需要合并成相同颜色 k。
枚举 k:
k='a':右区间 dp[1][2][a]=INF,不可行。
k='b':右区间 dp[1][2][b]=INF,不可行。 - mid=1:左 [0,1]("ab")全部 INF,不可行。
所以 dp[0][2][b]=INF。
最终,所有 dp[0][2][*] 都是 INF,返回 -1。确实,对于 "aba",没有相邻同色字符,无法进行任何合并,所以无法合并成一个字符。
总结
这个问题的核心在于通过区间 DP 记录每个区间合并成不同颜色的最小成本,并在合并时枚举中间颜色和分割点。关键点是理解合并操作的限制(只合并同色相邻字符)以及新颜色可以任意选择的特点。通过自底向上的 DP 计算,最终得到最小成本或判断不可行。