2026-07-02:将一个字符串排序的最小操作次数。用go语言,问题:给定只包含小写字母的字符串 s。你可以反复进行操作:每次选取 s 的一个

网易专栏1个月前发布 nxnqh
37 0 0

🤖 AI总结

主题

计算将字符串排序的最小操作次数

摘要

本文讲解如何计算将字符串通过子串排序变为升序所需的最小操作次数,分析多种情况并给出Go、Python、C++代码实现。

关键信息

  • 1 通过多次操作子串排序使字符串升序
  • 2 分六种情况判断操作次数
  • 3 时间复杂度O(n)

2026-07-02:将一个字符串排序的最小操作次数。用go语言,问题:给定只包含小写字母的字符串 s。你可以反复进行操作:每次选取 s 的一个连续子串,但子串不能覆盖整个字符串;然后把这个子串内部的字符按“从小到大不降序”重新排列。目标是通过尽可能少的这种操作,使整个字符串最终变成非降序(即从左到右字母不减)。

要求:计算实现上述目标所需的最小操作次数;如果无论怎么操作都无法做到,则输出 -1。

1 <= s.length <= 100000。

s 仅由小写英文字母组成。

输入: s = “dog”。

输出: 1。

解释:

将子字符串 “og” 排序为 “go”。

现在,s = “dgo”,已按升序排列。因此,答案是 1。

题目来自力扣3863。

一、函数逻辑分步拆解(逐段流程详解)

整体逻辑是分6层条件逐层判断,从最优解(0次)到最差解(3次)依次匹配,匹配到即直接返回答案,无需后续判断。

步骤1:判断原字符串是否已经全局升序

1. 将字符串转为字节切片,调用有序校验函数检查完整数组是否非降;

  • 2. 若本身有序,不需要任何操作,直接返回操作次数0。

  • 3. 示例dog原始序列 d→o→g,o > g,不满足升序,进入下一步判断。

    步骤2:特殊边界判断——字符串长度等于2

    规则:长度为2时,可选子串只能是单个字符(排序单个字符无变化),不存在有效操作能调换两个字符,永远无法排序,直接返回-1。
    示例dog长度为3,跳过该分支,继续判断。

    步骤3:找出全局最小字符 mn、全局最大字符 mx

    遍历整个字符数组,提取全部字符里ASCII最小、最大的字母:

    • s=”dog” 字符ASCII:d=100,o=111,g=103

  • • mn = d(100),mx = o(111)

    步骤4:判断能否仅用1次操作完成排序(返回1)

    判定条件满足其一即可:

    1. 字符串第一个字符就是全局最小值 mn;
    含义:首字符已经是全串最小,只需要对除去首字符的后半段连续子串做一次升序排序,后半段有序后整体必然全局升序;

  • 2. 字符串最后一个字符就是全局最大值 mx;
    含义:末尾字符已经是全串最大,只需要对除去末尾字符的前半段连续子串做一次升序排序,前半段有序后整体必然全局升序。

    代入示例 “dog” 验证

    首字符 t[0] = d,正好等于全局最小值 mn,满足第一条判定条件,直接返回操作次数1,和题目输出结果一致。
    操作对应解释:选取后两位连续子串og排序为go,得到dgo全局升序,仅1次操作。

    补充该分支通用场景举例

    例1:s=”dxb”,mn=d在首位,排序[x,b]一次即可;
    例2:s=”bdz”,mx=z在末尾,排序[b,d]一次即可。

    步骤5:不满足1次操作,判断能否仅用2次操作完成排序(返回2)

    前置前提:首字符不是全局最小、末尾字符不是全局最大。
    遍历中间区间t[1 : n-1](去掉第一个、最后一个字符的中间所有字符):
    只要中间任意一个字符等于全局最小值mn 或 全局最大值mx,就返回2次操作。

    原理说明

    两种场景对应两次操作方案:

    1. 中间存在全局最小值mn:
    第一次操作:排序[0, n-2](除最后一位),把中间的最小值挪到字符串最左侧;
    第二次操作:排序[1, n-1](除第一位),将后半段全部理顺,整体有序;

  • 2. 中间存在全局最大值mx:
    第一次操作:排序[1, n-1](除第一位),把中间最大值挪到字符串最右侧;
    第二次操作:排序[0, n-2](除最后一位),理顺前半段,整体有序。

    步骤6:仅剩最坏场景,固定需要3次操作(返回3)

    前置全部条件都不满足时,唯一剩余特征:

    1. 第一个字符是全局最大值mx;

  • 2. 最后一个字符是全局最小值mn;

  • 3. 中间所有字符既不含全局最小mn,也不含全局最大mx。

    三次操作完整逻辑

    1. 第一次操作:排序子串[0, n-2](去掉末尾),将首位的全局最大值移动到倒数第二位;

  • 2. 第二次操作:排序子串[1, n-1](去掉首位),把全局最大值移到最后一位,同时把末尾的全局最小值挪到第二位;

  • 3. 第三次操作:再次排序子串[0, n-2],将第二位的全局最小值移动到第一位,整个字符串完全升序。

    二、无解场景总结

    仅一种情况输出-1:字符串长度严格等于2,无论字符如何排列,都无法通过规则内操作实现升序。

    三、时间复杂度分析 1. 各环节耗时拆解

    1. slices.IsSorted:完整遍历一次字符串,O(n);

  • 2. slices.Min、slices.Max:各遍历一次字符串,合计O(n);

  • 3. 中间区间循环t[1:n-1]:最坏遍历n-2个字符,O(n);
    所有步骤为顺序串行执行,无嵌套循环。

    总时间复杂度

    O(n),n为字符串长度,线性复杂度,可支持题目上限1e5长度输入。

    四、额外空间复杂度分析 空间占用来源

    仅创建一个字节切片t := []byte(s)存储字符串副本,占用n字节;其余变量(mn、mx、循环临时变量)为常数级空间,不随字符串长度变化。

    总额外空间复杂度

    O(n)
    补充优化说明:若不复制完整切片,仅通过索引读取原字符串字符,可优化为O(1)常数空间;当前代码实现采用切片拷贝,因此空间为线性O(n)。

    Go完整代码如下:

    package main

    import (
    "fmt"
    "slices"
    )

    func minOperations(s string)int {
    t := []byte(s)
    // s 已经是升序
    if slices.IsSorted(t) {
    return0
    }

    n := len(t)
    // 长为 2,无法排序
    if n == 2 {
    return-1
    }

    mn := slices.Min(t)
    mx := slices.Max(t)
    // 如果 s[0] 是最小值,排序 [1,n-1] 即可
    // 如果 s[n-1] 是最大值,排序 [0,n-2] 即可
    if t[0] == mn || t[n-1] == mx {
    return1
    }

    // 如果 [1,n-2] 中有最小值,那么先排序 [0,n-2],把最小值排在最前面,然后排序 [1,n-1] 即可
    // 如果 [1,n-2] 中有最大值,那么先排序 [1,n-1],把最大值排在最后面,然后排序 [0,n-2] 即可
    for _, ch := range t[1 : n-1] {
    if ch == mn || ch == mx {
    return2
    }
    }

    // 现在只剩下一种情况:s[0] 是最大值,s[n-1] 是最小值,且 [1,n-2] 不含最小值和最大值
    // 先排序 [0,n-2],把最大值排到 n-2
    // 然后排序 [1,n-1],把最大值排在最后面,且最小值排在 1
    // 最后排序 [0,n-2],把最小值排在最前面
    return3
    }

    func main() {
    s := "dog"
    result := minOperations(s)
    fmt.Println(result)
    }

    2026-07-02:将一个字符串排序的最小操作次数。用go语言,问题:给定只包含小写字母的字符串 s。你可以反复进行操作:每次选取 s 的一个

    Python完整代码如下:

    # -*-coding:utf-8-*-

    from typing import List

    def minOperations(s: str) -> int:
    t = list(s)
    n = len(t)
    # s 已经是升序
    if all(t[i] <= t[i+1] for i in range(n-1)):
    return0
    # 长为 2,无法排序
    if n == 2:
    return-1
    mn = min(t)
    mx = max(t)
    # 如果 s[0] 是最小值,排序 [1,n-1] 即可
    # 如果 s[n-1] 是最大值,排序 [0,n-2] 即可
    if t[0] == mn or t[n-1] == mx:
    return1
    # 如果 [1,n-2] 中有最小值,那么先排序 [0,n-2],把最小值排在最前面,然后排序 [1,n-1] 即可
    # 如果 [1,n-2] 中有最大值,那么先排序 [1,n-1],把最大值排在最后面,然后排序 [0,n-2] 即可
    for ch in t[1:n-1]:
    if ch == mn or ch == mx:
    return2
    # 现在只剩下一种情况:s[0] 是最大值,s[n-1] 是最小值,且 [1,n-2] 不含最小值和最大值
    # 先排序 [0,n-2],把最大值排到 n-2
    # 然后排序 [1,n-1],把最大值排在最后面,且最小值排在 1
    # 最后排序 [0,n-2],把最小值排在最前面
    return3

    def main():
    s = "dog"
    result = minOperations(s)
    print(result)

    if __name__ == "__main__":
    main()

    2026-07-02:将一个字符串排序的最小操作次数。用go语言,问题:给定只包含小写字母的字符串 s。你可以反复进行操作:每次选取 s 的一个

    C++完整代码如下:

      
    


    using namespace std;

    int minOperations(string s) {
    string t = s;
    int n = t.length();

    // s 已经是升序
    if (is_sorted(t.begin(), t.end())) {
    return0;
    }

    // 长为 2,无法排序
    if (n == 2) {
    return-1;
    }

    char mn = *min_element(t.begin(), t.end());
    char mx = *max_element(t.begin(), t.end());

    // 如果 s[0] 是最小值,排序 [1,n-1] 即可
    // 如果 s[n-1] 是最大值,排序 [0,n-2] 即可
    if (t[0] == mn || t[n-1] == mx) {
    return1;
    }

    // 如果 [1,n-2] 中有最小值,那么先排序 [0,n-2],把最小值排在最前面,然后排序 [1,n-1] 即可
    // 如果 [1,n-2] 中有最大值,那么先排序 [1,n-1],把最大值排在最后面,然后排序 [0,n-2] 即可
    for (int i = 1; i < n - 1; i++) {
    if (t[i] == mn || t[i] == mx) {
    return2;
    }
    }

    // 现在只剩下一种情况:s[0] 是最大值,s[n-1] 是最小值,且 [1,n-2] 不含最小值和最大值
    // 先排序 [0,n-2],把最大值排到 n-2
    // 然后排序 [1,n-1],把最大值排在最后面,且最小值排在 1
    // 最后排序 [0,n-2],把最小值排在最前面
    return3;
    }

    int main() {
    string s = "dog";
    int result = minOperations(s);
    cout << result << endl;
    return0;
    }

    2026-07-02:将一个字符串排序的最小操作次数。用go语言,问题:给定只包含小写字母的字符串 s。你可以反复进行操作:每次选取 s 的一个

    我们相信人工智能为普通人提供了一种“增强工具”,并致力于分享全方位的AI知识。在这里,您可以找到最新的AI科普文章、工具评测、提升效率的秘籍以及行业洞察。 欢迎关注“福大大架构师每日一题”,发消息可获得面试资料,让AI助力您的未来发展。

    © 版权声明

    相关文章