学习笔记
题目解析
题目描述:
给定一个由0和1组成的字符串,可以进行最多k次相邻字符交换操作,目标是通过这些操作使得最终得到的字符串字典序最小。
思路:
- 局部最优解:每次找到从当前位置到字符串末尾的最小字符,并尝试将其移动到当前位置。
- 操作次数:计算将该最小字符移动到当前位置所需的交换次数,并与k进行比较。
- 贪心策略:如果移动操作次数小于等于k,则进行移动操作,并更新k值;否则,不进行移动。
思考:
以字符串 "01010" 和 k=2 为例:
-
初始字符串:"01010"
-
找到从位置0开始的最小字符 '0',它在位置0,不需要移动。
-
查找位置1的最小字符 '0',它在位置2,需要交换1次(从位置2到位置1)。
- 交换后:"00101"(k=1)
-
查找位置2的最小字符 '1',它已经在位置2,不需要移动。
-
查找位置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-1
到i
(不包括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)
来移除元素并调整列表长度,但直接在这里使用需要注意理解其副作用。
知识总结
新知识点:
- 贪心算法:在每一步选择中都采取最好或最优(即最有利)的选择,从而希望能够导致结果是全局最好或最优的算法。
- 字符串操作:Python中字符串是不可变的,但可以通过转换为列表来进行修改。
- 切片操作:Python中列表的切片操作可以方便地获取或修改子列表。
理解:
贪心算法并不总是能得到全局最优解,但对于某些问题,贪心策略可以高效地找到近似最优解或最优解。
学习建议:
- 多练习贪心算法题目,理解其适用场景和限制。
- 掌握Python中字符串和列表的常用操作,特别是切片操作。
学习计划
刷题计划:
- 每日一题:每天至少在豆包AI刷题刷一道题目。
- 专题训练:每周选择一个专题(如字符串处理、图论等)进行集中训练。
- 错题回顾:每周回顾一次本周在豆包AI刷的题,并尝试解决。
利用错题:
- 分析错误原因,是算法理解错误还是实现细节出错。
- 将错题记录下来,并定期复习。
- 针对错误类型,查找相关资料或请教他人进行针对性学习。
工具运用
AI刷题功能:
- 解析查看:查看AI提供的详细解析,理解算法思路和实现方法。
- 代码检查:提交自己的代码,让AI检查是否有错误或可以优化的地方。
结合学习资源:
- 视频教程:观看相关算法的视频教程,加深理解。
- 书籍阅读:阅读算法和数据结构的经典书籍,如《算法导论》。
- 在线论坛:在力扣、GitHub、豆包社区等平台参与讨论,学习他人的解题思路和技巧。
学习建议:
- 将豆包AI刷题功能与其他学习资源相结合,形成完整的学习闭环。
- 定期总结自己的学习进度和收获,调整学习计划。
- 保持积极的学习态度,遇到困难时及时寻求帮助。