合并字符串的最小成本问题(带权值版本,允许插入、删除、替换,且操作有不同成本)
字数 2479 2025-12-11 02:09:08

合并字符串的最小成本问题(带权值版本,允许插入、删除、替换,且操作有不同成本)

题目描述
给你两个字符串 st,以及三种操作的成本:

  1. 插入一个字符,成本为 ins_cost
  2. 删除一个字符,成本为 del_cost
  3. 将一个字符替换成另一个字符,成本为 rep_cost(如果字符相同,则替换成本为 0)。

你需要将字符串 s 转换成字符串 t,求最小总成本。
注意:这里的“替换”操作允许将字符 s[i] 改成任意字符(包括与原来相同),但只有字符相同时成本为 0,否则为 rep_cost

例如:
s = "abc", t = "adc", ins_cost = 1, del_cost = 1, rep_cost = 1
最小成本是 1(将 'b' 替换为 'd')。


解题思路

这是一道经典的编辑距离问题的变种,但这里我们明确用区间动态规划的思路来推导,以加深对区间DP的理解。
我们可以定义子问题为:将 s 的某个前缀转换成 t 的某个前缀的最小成本。但为了更贴合“区间”的概念,我们考虑从两个字符串的任意子串角度出发。
然而,更常见的解法是二维DP,但我们可以把它看作是一种“双区间”DP:

  • 定义 dp[i][j] 为将 s 的前 i 个字符转换成 t 的前 j 个字符的最小成本。
  • 这里“区间”指的是 s[0..i-1]t[0..j-1] 这两个区间。

但为了更符合“区间DP”的框架,我们可以想象为同时对两个序列的区间进行操作,不过这类问题更常见的名称是“序列对齐”,而不是狭义的“单序列区间划分”。不过我们可以把它理解为在两个序列上的区间DP(即区间长度变化)。


详细步骤

1. 定义状态

dp[i][j] 表示将 s 的前 i 个字符(即 s[0..i-1])转换成 t 的前 j 个字符(即 t[0..j-1])的最小成本。
这里 ij 分别表示两个字符串的前缀长度,取值范围:

  • i 从 0 到 mms 的长度)
  • j 从 0 到 nnt 的长度)

初始条件:

  • dp[0][0] = 0(两个空串转换成本为0)
  • dp[i][0] = i * del_cost(删除 s 的前 i 个字符)
  • dp[0][j] = j * ins_cost(插入 t 的前 j 个字符到空串)

2. 状态转移方程

对于 dp[i][j],我们考虑最后一步操作:

  1. 删除 s 的第 i 个字符(即 s[i-1]):
    此时我们已经将 s[0..i-2] 转换成了 t[0..j-1],然后删除 s[i-1]
    成本 = dp[i-1][j] + del_cost

  2. 插入 t 的第 j 个字符(即 t[j-1]):
    此时我们已经将 s[0..i-1] 转换成了 t[0..j-2],然后插入 t[j-1]
    成本 = dp[i][j-1] + ins_cost

  3. 替换(或保留) s[i-1]t[j-1]
    我们先让 s[0..i-2] 转换成 t[0..j-2],然后看 s[i-1]t[j-1] 是否相同。

    • 如果相同,则不需要替换,成本 = dp[i-1][j-1]
    • 如果不同,则替换,成本 = dp[i-1][j-1] + rep_cost

取三者最小值:

\[dp[i][j] = \min\begin{cases} dp[i-1][j] + del\_cost \\ dp[i][j-1] + ins\_cost \\ dp[i-1][j-1] + (s[i-1] == t[j-1] \;?\; 0 : rep\_cost) \end{cases} \]


3. 递推顺序

因为 dp[i][j] 依赖 i-1, ji, j-1i-1, j-1,所以可以按 i 从 0 到 mj 从 0 到 n 的顺序计算。


4. 示例演算

s = "abc", t = "adc", ins_cost = 1, del_cost = 1, rep_cost = 1 为例。

初始化:

dp[0][0] = 0
dp[1][0] = 1  (删除a)
dp[2][0] = 2  (删除a,b)
dp[3][0] = 3
dp[0][1] = 1  (插入a)
dp[0][2] = 2
dp[0][3] = 3

计算 dp[1][1]

  • 删除:dp[0][1] + 1 = 1+1=2
  • 插入:dp[1][0] + 1 = 1+1=2
  • 替换/保留:s[0]=a, t[0]=a 相同,所以 dp[0][0] + 0 = 0
    最小值 = 0

计算 dp[1][2]

  • 删除:dp[0][2] + 1 = 2+1=3
  • 插入:dp[1][1] + 1 = 0+1=1
  • 替换:s[0]=a, t[1]=d 不同,dp[0][1] + 1 = 1+1=2
    最小值 = 1

继续计算完表格:

最终 dp[3][3] 计算:

  • 删除:dp[2][3] + 1 = 1+1=2
  • 插入:dp[3][2] + 1 = 2+1=3
  • 替换:s[2]=c, t[2]=c 相同,dp[2][2] + 0 = 1+0=1
    最小值 = 1

所以最小成本是 1。


5. 时间复杂度与空间复杂度

  • 时间复杂度:O(m*n)
  • 空间复杂度:可以优化到 O(min(m, n)) 的滚动数组,但为了清晰,用 O(m*n) 也可以。

总结

这道题虽然本质是编辑距离,但用区间DP的思想(两个序列的区间对齐)可以自然推导。
关键点:

  1. 定义状态为两个前缀的转换最小成本。
  2. 从最后一步操作(删除、插入、替换)推导转移方程。
  3. 注意相同字符时替换成本为0。

通过这个例子,你能够掌握如何处理两个序列之间的最小转换成本问题,并理解如何将操作成本融入状态转移中。

合并字符串的最小成本问题(带权值版本,允许插入、删除、替换,且操作有不同成本) 题目描述 给你两个字符串 s 和 t ,以及三种操作的成本: 插入一个字符,成本为 ins_cost 。 删除一个字符,成本为 del_cost 。 将一个字符替换成另一个字符,成本为 rep_cost (如果字符相同,则替换成本为 0)。 你需要将字符串 s 转换成字符串 t ,求最小总成本。 注意:这里的“替换”操作允许将字符 s[i] 改成任意字符(包括与原来相同),但只有字符相同时成本为 0,否则为 rep_cost 。 例如: s = "abc" , t = "adc" , ins_cost = 1 , del_cost = 1 , rep_cost = 1 。 最小成本是 1(将 'b' 替换为 'd' )。 解题思路 这是一道经典的 编辑距离 问题的变种,但这里我们明确用 区间动态规划 的思路来推导,以加深对区间DP的理解。 我们可以定义子问题为:将 s 的某个前缀转换成 t 的某个前缀的最小成本。但为了更贴合“区间”的概念,我们考虑从两个字符串的任意子串角度出发。 然而,更常见的解法是 二维DP ,但我们可以把它看作是一种“双区间”DP: 定义 dp[i][j] 为将 s 的前 i 个字符转换成 t 的前 j 个字符的最小成本。 这里“区间”指的是 s[0..i-1] 和 t[0..j-1] 这两个区间。 但为了更符合“区间DP”的框架,我们可以想象为同时对两个序列的区间进行操作,不过这类问题更常见的名称是“序列对齐”,而不是狭义的“单序列区间划分”。不过我们可以把它理解为在两个序列上的区间DP(即区间长度变化)。 详细步骤 1. 定义状态 设 dp[i][j] 表示将 s 的前 i 个字符(即 s[0..i-1] )转换成 t 的前 j 个字符(即 t[0..j-1] )的最小成本。 这里 i 和 j 分别表示两个字符串的前缀长度,取值范围: i 从 0 到 m ( m 是 s 的长度) j 从 0 到 n ( n 是 t 的长度) 初始条件: dp[0][0] = 0 (两个空串转换成本为0) dp[i][0] = i * del_cost (删除 s 的前 i 个字符) dp[0][j] = j * ins_cost (插入 t 的前 j 个字符到空串) 2. 状态转移方程 对于 dp[i][j] ,我们考虑最后一步操作: 删除 s 的第 i 个字符(即 s[i-1] ): 此时我们已经将 s[0..i-2] 转换成了 t[0..j-1] ,然后删除 s[i-1] 。 成本 = dp[i-1][j] + del_cost 插入 t 的第 j 个字符(即 t[j-1] ): 此时我们已经将 s[0..i-1] 转换成了 t[0..j-2] ,然后插入 t[j-1] 。 成本 = dp[i][j-1] + ins_cost 替换 (或保留) s[i-1] 为 t[j-1] : 我们先让 s[0..i-2] 转换成 t[0..j-2] ,然后看 s[i-1] 和 t[j-1] 是否相同。 如果相同,则不需要替换,成本 = dp[i-1][j-1] 如果不同,则替换,成本 = dp[i-1][j-1] + rep_cost 取三者最小值: \[ dp[ i][ j ] = \min\begin{cases} dp[ i-1][ j ] + del\_cost \\ dp[ i][ j-1 ] + ins\_cost \\ dp[ i-1][ j-1] + (s[ i-1] == t[ j-1 ] \;?\; 0 : rep\_cost) \end{cases} \] 3. 递推顺序 因为 dp[i][j] 依赖 i-1, j 、 i, j-1 、 i-1, j-1 ,所以可以按 i 从 0 到 m , j 从 0 到 n 的顺序计算。 4. 示例演算 以 s = "abc" , t = "adc" , ins_cost = 1 , del_cost = 1 , rep_cost = 1 为例。 初始化: 计算 dp[1][1] : 删除: dp[0][1] + 1 = 1+1=2 插入: dp[1][0] + 1 = 1+1=2 替换/保留: s[0]=a , t[0]=a 相同,所以 dp[0][0] + 0 = 0 最小值 = 0 计算 dp[1][2] : 删除: dp[0][2] + 1 = 2+1=3 插入: dp[1][1] + 1 = 0+1=1 替换: s[0]=a , t[1]=d 不同, dp[0][1] + 1 = 1+1=2 最小值 = 1 继续计算完表格: 最终 dp[3][3] 计算: 删除: dp[2][3] + 1 = 1+1=2 插入: dp[3][2] + 1 = 2+1=3 替换: s[2]=c , t[2]=c 相同, dp[2][2] + 0 = 1+0=1 最小值 = 1 所以最小成本是 1。 5. 时间复杂度与空间复杂度 时间复杂度:O(m* n) 空间复杂度:可以优化到 O(min(m, n)) 的滚动数组,但为了清晰,用 O(m* n) 也可以。 总结 这道题虽然本质是编辑距离,但用区间DP的思想(两个序列的区间对齐)可以自然推导。 关键点: 定义状态为两个前缀的转换最小成本。 从最后一步操作(删除、插入、替换)推导转移方程。 注意相同字符时替换成本为0。 通过这个例子,你能够掌握如何处理两个序列之间的最小转换成本问题,并理解如何将操作成本融入状态转移中。