AI实践:知识点与用法详解之最小非递减子字符串成本 | 豆包MarsCode AI刷题

64 阅读5分钟

知识点与用法详解

1. 非递减子串

首先我们需要了解一个关键概念——非递减子串。在字符串中,非递减意味着字符按照从小到大的顺序排列(允许相等字符),例如字符串 "abb" 就是一个非递减子串,因为 a <= b <= b

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

对于给定的字符串,计算最长的非递减子串长度是解决本题的核心。常见的做法是通过遍历字符串,记录当前非递减子串的长度,如果遇到递减的情况(即当前字符小于前一个字符),就重置计数。

计算最长非递减子串的步骤

  • 初始化变量 max_len 为 1,因为至少会有一个字符本身是一个子串。
  • 使用变量 current_len 来记录当前非递减子串的长度,初始为 1。
  • 遍历字符串,对于每个字符 S[i],如果 S[i] >= S[i-1],则表示当前字符可以加入非递减子串,因此增加 current_len
  • 如果遇到 S[i] < S[i-1],则重置 current_len 为 1。
  • 在每次更新 current_len 后,更新 max_len,即保存最大的非递减子串长度。

代码示例

python

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

3. 字符串交换操作

在本题中,我们有最多一次交换相邻字符的操作。对于每个可能的交换位置,我们需要交换 S[i]S[i+1],并计算交换后字符串的最长非递减子串。

交换操作的实现

我们可以使用字符串的切片和拼接来实现交换操作。比如,交换 S[i]S[i+1] 可以通过以下方式:

swapped_S = S[:i] + S[i+1] + S[i] + S[i+2:]

这里,S[:i] 是交换前 i 之前的部分,S[i+1]S[i] 交换位置,S[i+2:] 是交换后的部分。

4. 暴力枚举法

本题通过暴力枚举每个可能的交换位置,计算交换后的字符串的最长非递减子串,并返回最小成本。

暴力枚举法的步骤

  1. 计算原始字符串的成本。
  2. 遍历所有可能的交换位置(从 i = 0i = N-2),对于每个交换位置,交换相邻字符并计算交换后的成本。
  3. 记录所有交换后的最小成本。
python

def solution(N: int, S: str) -> int:
    initial_cost = calculate_cost(S)  # 计算初始字符串的成本
    min_cost = initial_cost  # 初始最小成本
    for i in range(N - 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

5. 时间复杂度分析

  • calculate_cost 函数:遍历字符串一次,时间复杂度为 O(N),其中 N 是字符串的长度。
  • 暴力枚举:我们最多枚举 N-1 个交换位置,每次交换需要计算一次 calculate_cost,因此总体时间复杂度为 O(N^2)

在大多数情况下,暴力方法已经能够应对较小的字符串长度。对于非常长的字符串,我们可能需要优化算法。

6. 空间复杂度分析

由于我们使用的是常数空间来存储变量 max_lencurrent_lenswapped_S 等,因此空间复杂度为 O(1),不依赖于输入字符串的长度。

7. 改进与优化

尽管暴力方法能解决问题,但时间复杂度是 O(N^2),对于较长字符串可能表现不佳。可能的优化方向包括:

  • 局部优化:通过分析字符串中的相同字符区域或局部递减区域,减少交换次数,尽可能避免交换。
  • 高级数据结构:如果问题要求优化到 O(N)O(N log N),可以使用先进的数据结构,如 线段树树状数组,以在交换操作后高效地更新并查询最长非递减子串。

8. 边界情况处理

在实现时,特别需要注意边界情况:

  • 空字符串:应返回 0,因为没有非递减子串。
  • 字符串只有一个字符:应返回 1,因为单个字符本身就是一个非递减子串。
  • 字符串已经是非递减的:无需交换,直接返回原字符串的成本。

代码补充

python

def calculate_cost(S: str) -> int:
    if not S:  # 空字符串的情况
        return 0
    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

总结

  1. 非递减子串的概念:是指字符串中每个字符不小于前一个字符的子串。在本题中,我们需要计算字符串的最长非递减子串。
  2. 暴力枚举法:通过交换相邻字符并计算交换后的字符串成本,选择最小成本。
  3. 空间与时间复杂度:暴力方法的时间复杂度是 O(N^2),适用于较小规模的字符串,空间复杂度为 O(1)
  4. 优化方向:通过分析交换对字符串影响的规律,可以进一步减少交换次数和计算量。