字符串趋同最小代价问题 | 豆包MarsCode AI 刷题

63 阅读6分钟

字符串趋同最小代价问题 | 豆包MarsCode AI 刷题

问题描述

小U 和 小R 各自拥有一个长度相等的二进制字符串 AB。现在,他们想要将这两个字符串修改成相同的字符串。每次修改可以选择以下两种操作:

  1. 交换同一个字符串中的任意两个字符,交换操作的成本为它们索引之差的绝对值 |i - j|
  2. 对某个字符进行取反操作,取反的成本为 2。

小U 和 小R 想知道,将字符串 AB 修改为相同字符串的最小总成本是多少?


测试样例

样例1:

输入:str1 = "10001",str2 = "10000" 输出:2

样例2:

输入:str1 = "100100",str2 = "100001" 输出:2

样例3:

输入:str1 = "1010",str2 = "0111" 输出:3

样例4:

输入:str1 = "1100",str2 = "0011" 输出:4

解题思路


  1. 输入检查:确保输入字符串长度相等。
  2. 找出不同字符的位置:遍历字符串,记录不同字符的索引。
  3. 动态规划初始化:初始化动态规划数组,处理没有不同字符的情况。
  4. 动态规划更新:通过动态规划计算最小成本,考虑取反和交换操作。
  5. 返回结果:返回最小总成本。

代码详解


1.输入检查

首先检查输入的两个字符串 str1str2 的长度是否相等。如果不相等,抛出 ValueError 异常。

if len(str1) != len(str2):
        raise ValueError("Input strings must be of equal length")

2.找出不同字符的位置

遍历两个字符串,找出所有不同字符的索引,并将这些索引存储在列表 diff_id 中。

n = len(str1)
diff_id = []
for i in range(n):
    if str1[i] != str2[i]:
        diff_id.append(i)

3.动态规划初始化

  • 如果 diff_id 为空(即两个字符串完全相同),直接返回 0
  • 初始化一个动态规划数组 dp,其中 dp[i] 表示将前 i 个不同字符修改为相同字符的最小成本。
  • dp[1] 初始化为 2,表示第一个不同字符的取反成本。
m = len(diff_id)
if m == 0:
    return 0
# 初始化动态规划数组
dp = [0] * (m + 1)
dp[1] = 2  # 第一个不同字符的取反成本

4.动态规划更新

  • 对于每个不同的字符位置 i,计算取反的成本(dp[i - 1] + 2)。

  • 检查是否可以通过交换减少成本:

    • ​ 如果 str1[diff_id[i - 2]] != str1[diff_id[i - 1]],则计算交换的成本并更新 dp[i]

    • ​ 如果 i >= 4str1[diff_id[i - 3]] != str1[diff_id[i - 1]]str1[diff_id[i - 4]] != str1[diff_id[i - 2]],则计算四个字符的交换成本并更新 dp[i]

for i in range(2, m + 1):
        dp[i] = dp[i - 1] + 2  # 当前不同字符的取反成本
        # 检查是否可以通过交换减少成本
        if str1[diff_id[i - 2]] != str1[diff_id[i - 1]]:
            dp[i] = min(dp[i], dp[i - 2] + abs(diff_id[i - 1] - diff_id[i - 2]))
        
        # 检查是否可以通过交换减少成本(四个字符的情况)
        if i >= 4 and str1[diff_id[i - 3]] != str1[diff_id[i - 1]] and str1[diff_id[i - 4]] != str1[diff_id[i - 2]]:
            dp[i] = min(dp[i], dp[i - 4] + abs(diff_id[i - 1] - diff_id[i - 3]) + abs(diff_id[i - 2] - diff_id[i - 4]))

知识总结:


  1. 字符串操作

    • 字符串长度:使用 len(str1) 获取字符串的长度。
    • 字符串比较:使用 str1[i] != str2[i] 比较两个字符串在相同位置的字符是否不同。
  2. 列表操作

    • 列表初始化:使用 diff_id = [] 初始化一个空列表。
    • 列表追加:使用 diff_id.append(i) 将元素追加到列表中。
    • 列表长度:使用 len(diff_id) 获取列表的长度。
  3. 动态规划

    • 动态规划数组初始化:使用 dp = [0] * (m + 1) 初始化一个长度为 m + 1 的动态规划数组。
    • 状态转移:使用 dp[i] = dp[i - 1] + 2 表示当前不同字符的取反成本。
    • 最小成本选择:使用 min(dp[i], dp[i - 2] + abs(diff_id[i - 1] - diff_id[i - 2])) 选择最小成本的操作。
  4. 条件判断

    • 条件语句:使用 if len(str1) != len(str2): 检查两个字符串的长度是否相等。

    • 多条件判断:使用 if i >= 4 and str1[diff_id[i - 3]] != str1[diff_id[i - 1]] and str1[diff_id[i - 4]] != str1[diff_id[i - 2]]: 进行多条件判断。

  5. 异常处理

    • 抛出异常:使用 raise ValueError("Input strings must be of equal length") 在输入字符串长度不相等时抛出异常
  6. 测试用例

    • 测试函数:使用 print(solution("10001", "10000") == 2) 进行测试,验证代码的正确性。

学习计划:


1.定期刷题:每天至少一道算法题,思考如何在第一步的基础上,如何优化算法,降低时间复杂度。

2.错题总结:总结一下这道题考察的知识点、切入的角度、同类型的题目等,还要思考有没有更优的办法,代码还能不能更加简洁一些。

工具运用:


豆包MarsCode AI提供了详细的解题思路和代码示例。

向豆包提出问题,可以获得思路启发,便于我们更好的理解和把握如何运用算法去解题。

针对代码中可能出现的语法错误,逻辑漏洞等问题,可以帮忙检查出并给出修改建议,提升代码的准确率和效率

在解完算法题后,可以帮你回顾题目中涉及到的算法知识,数据结构知识等,强化你对这些知识点的掌握程度。并且为你拓展相关的,更深入或者更广泛的算法知识内容,拓展知识面,更好的应对更多类型的算法题。

完整代码实现


def solution(str1, str2):
    # 检查输入字符串长度是否相等
    if len(str1) != len(str2):
        raise ValueError("Input strings must be of equal length")
    n = len(str1)
    diff_id = []
    for i in range(n):
        if str1[i] != str2[i]:
            diff_id.append(i)
    m = len(diff_id)
    if m == 0:
        return 0 
    # 初始化动态规划数组
    dp = [0] * (m + 1)
    dp[1] = 2  # 第一个不同字符的取反成本
    for i in range(2, m + 1):
        dp[i] = dp[i - 1] + 2  # 当前不同字符的取反成本
        # 检查是否可以通过交换减少成本
        if str1[diff_id[i - 2]] != str1[diff_id[i - 1]]:
            dp[i] = min(dp[i], dp[i - 2] + abs(diff_id[i - 1] - diff_id[i - 2]))
        # 检查是否可以通过交换减少成本(四个字符的情况)
        if i >= 4 and str1[diff_id[i - 3]] != str1[diff_id[i - 1]] and str1[diff_id[i - 4]] != str1[diff_id[i - 2]]:
            dp[i] = min(dp[i], dp[i - 4] + abs(diff_id[i - 1] - diff_id[i - 3]) + abs(diff_id[i - 2] - diff_id[i - 4]))
    return dp[m]

if __name__ == "__main__":
    # Add your test cases here
    print(solution("10001", "10000") == 2)
    print(solution("100100", "100001") == 2)
    print(solution("1010", "0011") == 3)
    print(solution("1100", "0011") == 4)