合并相邻同色字符的最小操作次数问题(每次合并两个相邻同色字符,合并产生新字符,新字符颜色可不同,每次合并有操作成本)
问题描述
给定一个字符串 s,长度为 n,每个字符有一个颜色(用一个小写英文字母表示)。初始字符串为 s[0..n-1]。你可以重复执行以下操作:
- 选择两个相邻且颜色相同的字符
s[i]和s[i+1]。 - 将它们合并为一个新字符,新字符的颜色可以与原来的颜色不同(也可以相同,具体颜色由规则表给出)。
- 此操作会消耗一定的操作次数(合并成本),具体成本由规则表给出。
- 合并后,原来两个字符消失,新字符占据它们的位置,字符串长度减少 1。
你的目标是通过一系列这样的操作,将整个字符串最终合并为一个字符,并且最小化总操作次数。
你需要知道规则表:
- 用
(a, b, c, cost)表示:当合并两个颜色均为a的字符时,可以生成颜色为c的新字符,且这次合并的操作成本为cost。 - 注意:对于同一种输入颜色
a,可能有多种输出颜色c的选择,且对应不同成本。 - 题目保证至少有一种方式可以将字符串合并为一个字符。
举例:
假设字符串为 "aa",颜色规则为:(a, a, a, 2) 和 (a, a, b, 3)。
- 如果选择合并为
a,成本 = 2,最终字符为a。 - 如果选择合并为
b,成本 = 3,最终字符为b。
最终答案为 2。
如果字符串更长,你需要选择合并顺序和每次合并产生的新颜色,使得总成本最小。
思路分析
这是一个典型的区间动态规划(Interval DP)问题,因为每次合并会减少一个字符,最终要合并整个区间为一个字符。我们可以定义状态来表示一个区间合并成某种颜色的最小成本。
状态定义:
dp[i][j][c] = 将区间 s[i..j] 合并成一个颜色为 c 的字符所需的最小操作次数。
其中 i 和 j 是区间端点(0 ≤ i ≤ j < n),c 是颜色(例如 'a' 到 'z' 共 26 种颜色)。
最终答案:
合并整个区间 [0, n-1] 成一个字符的最小成本,即
min{ dp[0][n-1][c] 对所有颜色 c }。
状态转移思路:
考虑区间 [i, j] 如何合并成一个颜色为 c 的字符。
最后一步操作一定是将区间内最后两个合并成的字符(它们各自已经是某个颜色)再进行一次合并,得到颜色 c。
但我们需要知道这两个字符的颜色是什么。
我们可以这样分解:
- 假设区间
[i, j]在最后一步合并时,左半部分合并成颜色x,右半部分合并成颜色y,且x和y可以合并成颜色c(根据规则表)。 - 左半部分区间是
[i, k],右半部分区间是[k+1, j],其中k是分界点(i ≤ k < j)。 - 那么总成本 = 将
[i, k]合并成颜色x的最小成本 + 将[k+1, j]合并成颜色y的最小成本 + 合并(x, y, c)的成本。
但要注意:本题规定合并操作只能合并两个相邻的同色字符,而不是任意两个相邻字符。
那么这里 x 和 y 必须相同,才能合并成 c。
所以最后一步合并时,左半部分和右半部分的颜色必须相同,设为 x,然后根据规则 (x, x, c, cost) 合并成颜色 c。
因此状态转移方程为:
dp[i][j][c] = min {
for k from i to j-1:
for color x in 所有颜色:
if 存在规则 (x, x, c, cost):
dp[i][j][c] = min(dp[i][j][c], dp[i][k][x] + dp[k+1][j][x] + cost)
}
其中 dp[i][k][x] 表示区间 [i, k] 合并成颜色 x 的最小成本,同理 dp[k+1][j][x]。
边界条件:
区间长度为 1 时,即 i == j:
- 如果
c与s[i]的颜色相同,则dp[i][i][c] = 0(不需要合并,已经是这个颜色)。 - 否则
dp[i][i][c] = INF(不可能直接得到一个不同颜色的单字符,因为没有合并操作)。
具体步骤
假设颜色集合为 colors = {'a', 'b', ..., 'z'} 中的一部分,规则表用 rules 存储,每条规则是 (x, x, c, cost) 表示两个颜色为 x 的字符合并成颜色 c 的成本为 cost。
-
初始化:
- 设
n = len(s)。 - 定义
dp[n][n][numColors]为一个大数组,初始值为INF(一个很大的数,表示不可能)。 - 对于每个
i从 0 到 n-1:- 设
color_i = s[i]的颜色。 - 对每个颜色
c:- 如果
c == color_i,dp[i][i][c] = 0。 - 否则
dp[i][i][c] = INF。
- 如果
- 设
- 设
-
递推计算:
区间长度len从 2 到 n:- 对每个起点
i,j = i + len - 1:- 对每个颜色
c:- 对每个分割点
k从i到j-1:- 对每个颜色
x:- 如果存在规则
(x, x, c, cost):- 如果
dp[i][k][x] != INF且dp[k+1][j][x] != INF:total = dp[i][k][x] + dp[k+1][j][x] + cost- 更新
dp[i][j][c] = min(dp[i][j][c], total)
- 如果
- 如果存在规则
- 对每个颜色
- 对每个分割点
- 对每个颜色
- 对每个起点
-
答案:
ans = min{ dp[0][n-1][c] 对所有颜色 c }。
示例演示
输入:
s = "aab"
颜色规则:
(a, a, a, 1) // 两个 a 合并成 a,成本 1
(a, a, b, 2) // 两个 a 合并成 b,成本 2
(b, b, a, 3) // 两个 b 合并成 a,成本 3
(b, b, b, 1) // 两个 b 合并成 b,成本 1
只有颜色 a 和 b 可能出现。
逐步计算:
-
初始化:
dp[0][0]['a'] = 0,dp[0][0]['b'] = INF。dp[1][1]['a'] = 0,dp[1][1]['b'] = INF。dp[2][2]['b'] = 0,dp[2][2]['a'] = INF。
-
区间长度 2:
- 区间 [0,1]("aa"):
- 合并成颜色 a:可以左 a 右 a 合并成 a(成本 1),
dp[0][1]['a'] = 0+0+1 = 1。 - 合并成颜色 b:可以左 a 右 a 合并成 b(成本 2),
dp[0][1]['b'] = 0+0+2 = 2。
- 合并成颜色 a:可以左 a 右 a 合并成 a(成本 1),
- 区间 [1,2]("ab"):
- 注意左右颜色不同(a 和 b),无法直接合并,必须先在内部合并成长度为 1 的某种颜色,再与另一边合并。但区间长度为 2 时,必须直接合并左右两个字符,这里左右颜色不同,所以不可能合并成一个字符,所以
dp[1][2][a] = INF,dp[1][2][b] = INF。
- 注意左右颜色不同(a 和 b),无法直接合并,必须先在内部合并成长度为 1 的某种颜色,再与另一边合并。但区间长度为 2 时,必须直接合并左右两个字符,这里左右颜色不同,所以不可能合并成一个字符,所以
- 区间 [0,1]("aa"):
-
区间长度 3 [0,2]("aab"):
- 目标颜色 a:
- 分割点 k=0:左 [0,0] 右 [1,2]。
- 左颜色必须 = 右颜色 = x,才能合并成 a。
- 对 x=a:左 dp[0][0]['a']=0,右 dp[1][2]['a']=INF → 不可能。
- 对 x=b:左 dp[0][0]['b']=INF → 不可能。
- 分割点 k=1:左 [0,1] 右 [2,2]。
- 对 x=a:左 dp[0][1]['a']=1,右 dp[2][2]['a']=INF → 不可能。
- 对 x=b:左 dp[0][1]['b']=2,右 dp[2][2]['b']=0 → 总 = 2+0=2,再看规则 (b,b,a,3) 可以合并成 a,成本 3,所以总=2+0+3=5 → 更新 dp[0][2]['a']=5。
- 分割点 k=0:左 [0,0] 右 [1,2]。
- 目标颜色 b:
- 分割点 k=0:
- 对 x=a:左 dp[0][0]['a']=0,右 dp[1][2]['a']=INF → 不可能。
- 对 x=b:左 INF → 不可能。
- 分割点 k=1:
- 对 x=a:左 1,右 INF → 不可能。
- 对 x=b:左 2,右 0 → 总=2,规则 (b,b,b,1) 合并成 b,成本 1,总=2+0+1=3 → 更新 dp[0][2]['b']=3。
- 分割点 k=0:
- 目标颜色 a:
-
答案:
合并整个区间成一个字符的最小成本 = min(dp[0][2]['a'], dp[0][2]['b']) = min(5, 3) = 3。
解释:
一种最小成本的合并顺序:
- 先合并前两个 'a' 为 'b'(成本 2),字符串变成 "bb"。
- 合并两个 'b' 为 'b'(成本 1),字符串变成 "b"。
总成本 3。
总结
这个问题的核心在于:
- 状态定义为
dp[i][j][c]表示区间[i, j]合并成单一颜色c的最小成本。 - 状态转移时,必须保证最后合并的两个子区间颜色相同,然后根据规则合并成目标颜色。
- 需要枚举分界点、左半部分的颜色、右半部分的颜色(必须相同)以及目标颜色。
- 时间复杂度 O(n^3 * m^2),其中 n 是字符串长度,m 是颜色种类数(通常 ≤ 26),在合理范围内可解。
通过这个例子,你可以看到区间动态规划在处理合并类问题时的通用思路:从小区间合并到大区间,记录区间合并成各种可能状态的最优值。