我的刷题之旅 | 豆包MarsCode AI刷题

2 阅读6分钟

学习笔记

题目解析

题目描述
给定一个由0和1组成的字符串,可以进行最多k次相邻字符交换操作,目标是通过这些操作使得最终得到的字符串字典序最小。

思路

  1. 局部最优解:每次找到从当前位置到字符串末尾的最小字符,并尝试将其移动到当前位置。
  2. 操作次数:计算将该最小字符移动到当前位置所需的交换次数,并与k进行比较。
  3. 贪心策略:如果移动操作次数小于等于k,则进行移动操作,并更新k值;否则,不进行移动。

思考
以字符串 "01010" 和 k=2 为例:

  1. 初始字符串:"01010"

  2. 找到从位置0开始的最小字符 '0',它在位置0,不需要移动。

  3. 查找位置1的最小字符 '0',它在位置2,需要交换1次(从位置2到位置1)。

    • 交换后:"00101"(k=1)
  4. 查找位置2的最小字符 '1',它已经在位置2,不需要移动。

  5. 查找位置3的最小字符 '0',它在位置4,需要交换1次,但k=1,只能再进行一次操作,所以不进行移动。

代码详解: 当然,我们可以将代码分块进行详细解释。以下是solution函数的逐步分析:

函数定义和初始化

	def solution(n: int, k: int, s: str) -> str:

	    s = list(s)  # 将字符串转换为列表以便进行交换操作
  • def solution(n: int, k: int, s: str) -> str: 定义了函数solution,它接受三个参数:字符串的长度n(虽然这个参数在函数内部并未直接使用,但可能是为了保持接口的一致性或后续扩展),最大操作次数k,以及字符串s
  • s = list(s) 将输入的字符串s转换为列表,因为字符串在Python中是不可变的,而列表是可变的,允许我们进行元素交换等操作。

主循环:遍历字符串的每个位置

	for i in range(n):
  • for i in range(n): 遍历字符串的每个字符位置,从0到n-1

查找当前位置之后的最小字符及其索引

	min_char = min(s[i:])  # 找到从当前位置 i 到末尾的最小字符

	        min_index = s.index(min_char, i)  # 找到最小字符在当前位置之后的第一次出现位置
  • min_char = min(s[i:]) 从当前位置i开始,找到字符串s剩余部分的最小字符。
  • min_index = s.index(min_char, i) 在字符串s中,从位置i开始查找最小字符min_char的第一次出现位置,并赋值给min_index

判断并执行交换操作

	swaps_needed = min_index - i  # 计算将 min_char 移动到位置 i 所需的交换次数

	        

	        if swaps_needed <= k:

	            # 将 min_char 移动到位置 i

	            # 注意:这里的实现需要小心处理,因为直接切片赋值会有问题

	            # 下面的代码是原始代码,存在问题,将用注释指出并给出正确实现

	            # s[i], s[i+1:min_index+1] = s[min_index], s[i:min_index]

	            

	            # 正确实现:通过循环交换元素

	            for j in range(min_index-1, i-1, -1):

	                s[j], s[j-1] = s[j-1], s[j]

	            

	            k -= swaps_needed  # 更新剩余操作次数

	        else:

	            # 如果交换次数超过 k,则不进行交换

	            continue
  • swaps_needed = min_index - i 计算将最小字符从min_index位置移动到i位置所需的交换次数。

  • if swaps_needed <= k: 如果所需交换次数小于等于剩余操作次数k,则执行交换操作。

    • 错误实现s[i], s[i+1:min_index+1] = s[min_index], s[i:min_index] 这行代码是有问题的,因为它会覆盖列表的一部分而不是正确地交换元素。
    • 正确实现:通过循环从min_index-1i(不包括i-1,因为j-1会访问到它),逐步将元素向左移动一个位置,从而将最小字符移动到位置i
  • k -= swaps_needed 更新剩余操作次数k

  • else: continue 如果所需交换次数大于剩余操作次数k,则不进行任何操作,继续下一次循环。

返回结果

	return ''.join(s)  # 将修改后的列表转换回字符串并返回
  • return ''.join(s) 将修改后的列表s转换回字符串,并返回作为函数的最终结果。

注意

  • 上述代码中的切片操作 s[i+1:min_index+1] = s[i:min_index] 实际上是有问题的,因为切片赋值会导致原列表部分被覆盖,且长度不会改变。这里应使用循环交换或者重新构建部分列表。
  • 简化版代码中通过 s.pop(min_index) 来移除元素并调整列表长度,但直接在这里使用需要注意理解其副作用。

知识总结

新知识点

  1. 贪心算法:在每一步选择中都采取最好或最优(即最有利)的选择,从而希望能够导致结果是全局最好或最优的算法。
  2. 字符串操作:Python中字符串是不可变的,但可以通过转换为列表来进行修改。
  3. 切片操作:Python中列表的切片操作可以方便地获取或修改子列表。

理解
贪心算法并不总是能得到全局最优解,但对于某些问题,贪心策略可以高效地找到近似最优解或最优解。

学习建议

  • 多练习贪心算法题目,理解其适用场景和限制。
  • 掌握Python中字符串和列表的常用操作,特别是切片操作。

学习计划

刷题计划

  1. 每日一题:每天至少在豆包AI刷题刷一道题目。
  2. 专题训练:每周选择一个专题(如字符串处理、图论等)进行集中训练。
  3. 错题回顾:每周回顾一次本周在豆包AI刷的题,并尝试解决。

利用错题

  • 分析错误原因,是算法理解错误还是实现细节出错。
  • 将错题记录下来,并定期复习。
  • 针对错误类型,查找相关资料或请教他人进行针对性学习。

工具运用

AI刷题功能

  • 解析查看:查看AI提供的详细解析,理解算法思路和实现方法。
  • 代码检查:提交自己的代码,让AI检查是否有错误或可以优化的地方。

结合学习资源

  • 视频教程:观看相关算法的视频教程,加深理解。
  • 书籍阅读:阅读算法和数据结构的经典书籍,如《算法导论》。
  • 在线论坛:在力扣、GitHub、豆包社区等平台参与讨论,学习他人的解题思路和技巧。

学习建议

  • 将豆包AI刷题功能与其他学习资源相结合,形成完整的学习闭环。
  • 定期总结自己的学习进度和收获,调整学习计划。
  • 保持积极的学习态度,遇到困难时及时寻求帮助。