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

47 阅读5分钟

题目解析与解决方案

背景介绍

本题的目标是计算一个由字符 'a' 和 'b' 组成的字符串 S 的“成本”。这里的成本定义为字符串中按非递减顺序排列的最长子字符串的长度。允许进行最多一次交换操作,即可以交换字符串中的任意一对相邻字符,目的是找到在执行最多一次交换操作后,字符串 S 的最小可能成本。

成本的定义

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

解题思路

  1. 计算原始字符串的成本:首先,我们需要计算未经任何交换的原始字符串的成本。
  2. 尝试所有可能的交换:接着,我们枚举所有可能的交换位置,计算每次交换后的成本,并记录其中的最小值。
  3. 返回最小成本:最后,返回上述过程中的最小成本作为最终答案。

关键点

  • 计算字符串的成本:即计算其最长的非递减子串。
  • 交换操作的影响:通过交换每对相邻字符,检查是否可以使非递减子串变长。

代码详解

以下是 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

代码分析

  • calculate_cost 函数:该函数用于计算一个字符串中最长的非递减子串的长度。通过遍历字符串,在遇到不满足非递减条件时重置子串长度,并记录最长的非递减子串。
  • solution 函数
    • 首先,计算原始字符串的成本 initial_cost
    • 然后,遍历所有可能的相邻字符交换位置,交换字符后再计算新的成本 swapped_cost,并更新最小成本 min_cost

知识总结

  • 最长非递减子串的计算:通过遍历字符串并比较相邻的字符,累积非递减子串的长度,遇到递减时重置长度。
  • 交换操作的影响:交换操作可能影响到最长非递减子串的长度。通过交换相邻字符,可以尝试将一些字符连接成更长的非递减子串,从而降低字符串的成本。
  • 暴力枚举与优化:由于题目限制最多只允许交换一次,我们采用了暴力枚举每个交换位置的策略,并对每个可能的交换计算交换后的成本。虽然这种方式在最坏情况下时间复杂度为 O(N^2),但对于一般情况已经能够有效处理。

学习方法与心得

  • 逐步拆解问题:在开始编码之前,需要仔细理解题目中的关键定义,如“成本”的定义(最长非递减子串的长度)。然后,可以将问题拆解为多个小问题,逐一解决。
  • 数据结构的使用:本题没有涉及复杂的数据结构,主要通过字符串的遍历和简单的变量记录来实现。然而,在处理类似的问题时,理解如何高效地计算某种属性(如非递减子串的长度)是非常重要的。
  • 问题调试和优化:在实际编程过程中,调试是不可避免的。代码中可能会遇到边界情况或者交换后字符串未正确更新的情况。通过逐步调试和打印中间结果,可以帮助发现问题。

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

  • 交换后的字符串计算错误:在交换字符后,确保字符串正确更新。可以通过调试打印交换后的字符串,验证交换是否生效。
  • 计算非递减子串时漏掉边界情况:需要注意字符串只有一个字符或为空的特殊情况,这些情况下成本应该为 1 或 0。

总结

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