最小非递减字符串成本 | 豆包MarsCode AI刷题

157 阅读6分钟

题目解析

本题要求我们计算一个由字符 ab 组成的字符串 S 的“成本”,成本定义为字符串中按非递减顺序排列的最长子字符串的长度。我们可以进行最多一次交换操作,目标是找到在执行最多一次交换操作后,字符串 S 的最小可能成本。

解题思路

  1. 成本的定义
    字符串的成本是其最长非递减子串的长度。例如,字符串 "abba" 的最长非递减子串是 "bb",所以成本为 2。通过交换相邻的两个字符,我们有可能使某些子串变长,从而减少字符串的成本。

  2. 交换操作的影响
    我们最多可以交换字符串中的一对相邻字符。交换后,我需要重新计算字符串的成本,看是否比原始字符串的成本更低。

  3. 步骤

    • 先计算原始字符串的成本。
    • 然后枚举所有可能的交换位置,计算交换后的成本。
    • 返回交换后的最小成本。
  4. 关键点

    • 计算一个字符串的“成本”即计算其最长的非递减子串。
    • 然后,通过交换每对相邻字符来查看是否能使非递减子串变长。

代码详解

python

def solution(N: int, S: str) -> int:
    # 计算字符串 S 中最长非递减子字符串的长度
    def calculate_cost(S: str) -> int:
        max_len = 1
        current_len = 1
        for i in range(1, len(S)):
            if S[i] >= S[i-1]:  # 如果当前字符不小于前一个字符
                current_len += 1
                max_len = max(max_len, current_len)
            else:
                current_len = 1  # 重置当前非递减子字符串的长度
        return max_len
    
    # 计算初始字符串 S 的成本
    initial_cost = calculate_cost(S)
    
    # 尝试所有可能的交换位置
    min_cost = initial_cost
    for i in range(N - 1):
        # 交换 S[i] 和 S[i+1]
        swapped_S = S[:i] + S[i+1] + S[i] + S[i+2:]
        # 计算交换后的成本
        swapped_cost = calculate_cost(swapped_S)
        # 更新最小成本
        min_cost = min(min_cost, swapped_cost)
    
    return min_cost

# 测试用例
if __name__ == '__main__':
    print(solution(N = 4, S = "abba") == 2)  # 交换 "ab" -> "ba" 后,最长非递减子串是 "bb" 长度为 2
    print(solution(N = 5, S = "baabb") == 2)  # 交换 "ba" -> "ab" 后,最长非递减子串是 "aa" 长度为 2
    print(solution(N = 3, S = "bab") == 2)   # 交换 "ba" -> "ab" 后,最长非递减子串是 "ab" 或 "ba" 长度为 2

代码分析

  1. calculate_cost 函数
    该函数用于计算一个字符串中最长的非递减子串的长度。我通过遍历字符串,在遇到不满足非递减条件时重置子串长度,并记录最长的非递减子串。

  2. solution 函数

    • 首先,计算原始字符串的成本 initial_cost
    • 然后,遍历所有可能的相邻字符交换位置,交换字符后再计算新的成本 swapped_cost,并更新最小成本。

知识总结

  1. 最长非递减子串的计算

    • 这个问题的关键在于如何计算一个字符串的最长非递减子串。我们可以通过遍历字符串并比较相邻的字符,累积非递减子串的长度,遇到递减时重置长度。
  2. 交换操作的影响

    • 交换操作可能影响到最长非递减子串的长度。通过交换相邻字符,我们能够尝试将一些字符连接成更长的非递减子串,从而降低字符串的成本。
  3. 暴力枚举与优化

    • 由于题目限制最多只允许交换一次,我们采用了暴力枚举每个交换位置的策略,并对每个可能的交换计算交换后的成本。虽然这种方式在最坏情况下时间复杂度为 O(N^2),但是对一般情况已经能够有效处理。

学习方法与心得

  1. 逐步拆解问题

    • 在开始编码之前,我们需要仔细理解题目中的关键定义,如“成本”的定义(最长非递减子串的长度)。然后,我们可以将问题拆解为多个小问题,逐一解决。
  2. 数据结构的使用

    • 本题没有涉及复杂的数据结构,主要通过字符串的遍历和简单的变量记录来实现。然而,在处理类似的问题时,理解如何高效地计算某种属性(如非递减子串的长度)是非常重要的。
  3. 问题调试和优化

    • 在实际编程过程中,调试是不可避免的。代码中可能会遇到边界情况或者交换后字符串未正确更新的情况。通过逐步调试和打印中间结果,可以帮助发现问题。

学习建议

  1. 理解问题的本质

    • 在解决编程题时,首先需要理解题目要求的核心问题,并仔细分析题目中的每个条件。例如,本题的核心就是“计算最长非递减子串的长度”,理解这一点之后,我们可以开始思考如何高效地计算这个长度。
  2. 从简单的暴力方法开始

    • 在面对较为复杂的问题时,可以先使用暴力方法实现基本功能,确保能正确解决问题,再考虑如何优化。如果暴力方法已经能够在给定数据范围内正常工作,就可以暂时不考虑优化。
  3. 多做练习,提升算法设计能力

    • 通过不断刷题,我们可以积累更多的算法技巧和解题思路,同时提升对时间复杂度和空间复杂度的敏感度。豆包MarsCode AI提供了丰富的题库,可以帮助巩固算法基础并解决各种类型的问题。

可能出现的问题及解决办法

  1. 交换后的字符串计算错误

    • 在交换字符后,确保字符串正确更新。可以通过调试打印交换后的字符串,验证交换是否生效。
  2. 计算非递减子串时漏掉边界情况

    • 需要注意字符串只有一个字符或为空的特殊情况,这些情况下成本应该为 1 或 0。

总结:

通过本题的学习,我不仅掌握了计算最长非递减子串的方法,还了解了如何通过交换操作影响字符串的结构,并通过暴力方法解决问题。学习过程中,不仅要注重算法本身,还应注重对边界情况的处理、复杂度分析以及如何优化和提高算法的性能。