合并相邻同色字符的最小操作次数问题(每次合并两个相邻同色字符,合并产生新字符,新字符颜色可不同,每次合并有操作成本)
字数 3814 2025-12-21 08:59:13

合并相邻同色字符的最小操作次数问题(每次合并两个相邻同色字符,合并产生新字符,新字符颜色可不同,每次合并有操作成本)


问题描述

给定一个字符串 s,长度为 n,每个字符有一个颜色(用一个小写英文字母表示)。初始字符串为 s[0..n-1]。你可以重复执行以下操作:

  1. 选择两个相邻颜色相同的字符 s[i]s[i+1]
  2. 将它们合并为一个新字符,新字符的颜色可以与原来的颜色不同(也可以相同,具体颜色由规则表给出)。
  3. 此操作会消耗一定的操作次数(合并成本),具体成本由规则表给出。
  4. 合并后,原来两个字符消失,新字符占据它们的位置,字符串长度减少 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 的字符所需的最小操作次数。
其中 ij 是区间端点(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,且 xy 可以合并成颜色 c(根据规则表)。
  • 左半部分区间是 [i, k],右半部分区间是 [k+1, j],其中 k 是分界点(i ≤ k < j)。
  • 那么总成本 = 将 [i, k] 合并成颜色 x 的最小成本 + 将 [k+1, j] 合并成颜色 y 的最小成本 + 合并 (x, y, c) 的成本。

但要注意:本题规定合并操作只能合并两个相邻的同色字符,而不是任意两个相邻字符。
那么这里 xy 必须相同,才能合并成 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

  • 如果 cs[i] 的颜色相同,则 dp[i][i][c] = 0(不需要合并,已经是这个颜色)。
  • 否则 dp[i][i][c] = INF(不可能直接得到一个不同颜色的单字符,因为没有合并操作)。

具体步骤

假设颜色集合为 colors = {'a', 'b', ..., 'z'} 中的一部分,规则表用 rules 存储,每条规则是 (x, x, c, cost) 表示两个颜色为 x 的字符合并成颜色 c 的成本为 cost

  1. 初始化

    • n = len(s)
    • 定义 dp[n][n][numColors] 为一个大数组,初始值为 INF(一个很大的数,表示不可能)。
    • 对于每个 i 从 0 到 n-1:
      • color_i = s[i] 的颜色。
      • 对每个颜色 c
        • 如果 c == color_idp[i][i][c] = 0
        • 否则 dp[i][i][c] = INF
  2. 递推计算
    区间长度 len 从 2 到 n:

    • 对每个起点 ij = i + len - 1
      • 对每个颜色 c
        • 对每个分割点 kij-1
          • 对每个颜色 x
            • 如果存在规则 (x, x, c, cost)
              • 如果 dp[i][k][x] != INFdp[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)
  3. 答案
    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 可能出现。

逐步计算

  1. 初始化:

    • dp[0][0]['a'] = 0dp[0][0]['b'] = INF
    • dp[1][1]['a'] = 0dp[1][1]['b'] = INF
    • dp[2][2]['b'] = 0dp[2][2]['a'] = INF
  2. 区间长度 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
    • 区间 [1,2]("ab"):
      • 注意左右颜色不同(a 和 b),无法直接合并,必须先在内部合并成长度为 1 的某种颜色,再与另一边合并。但区间长度为 2 时,必须直接合并左右两个字符,这里左右颜色不同,所以不可能合并成一个字符,所以 dp[1][2][a] = INFdp[1][2][b] = INF
  3. 区间长度 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。
    • 目标颜色 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。
  4. 答案:
    合并整个区间成一个字符的最小成本 = 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),在合理范围内可解。

通过这个例子,你可以看到区间动态规划在处理合并类问题时的通用思路:从小区间合并到大区间,记录区间合并成各种可能状态的最优值

合并相邻同色字符的最小操作次数问题(每次合并两个相邻同色字符,合并产生新字符,新字符颜色可不同,每次合并有操作成本) 问题描述 给定一个字符串 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][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 } 。 示例演示 输入 : 只有颜色 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 。 区间 [ 1,2 ]("ab"): 注意左右颜色不同(a 和 b),无法直接合并,必须先在内部合并成长度为 1 的某种颜色,再与另一边合并。但区间长度为 2 时,必须直接合并左右两个字符,这里左右颜色不同,所以不可能合并成一个字符,所以 dp[1][2][a] = INF , dp[1][2][b] = INF 。 区间长度 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。 目标颜色 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。 答案: 合并整个区间成一个字符的最小成本 = 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),在合理范围内可解。 通过这个例子,你可以看到区间动态规划在处理合并类问题时的通用思路: 从小区间合并到大区间,记录区间合并成各种可能状态的最优值 。