字符串原地排序

字符串原地排序

问题背景

我们需要对一个仅包含英文字母和数字的字符串进行排序,但排序过程必须遵循一套特殊的规则,尤其需要保持原字符串中"字母"和"数字"的位置类型不变。

排序规则

  1. 位置类型保持不变 (Positional Integrity):

    这是最核心的规则。排序后的字符串,其每个位置上的字符类型必须与原字符串相同。

    • 如果原字符串中 inputStr[i] 是一个数字 ,那么排序后新字符串的第 i 个位置也必须是一个数字
    • 如果原字符串中 inputStr[i] 是一个字母 ,那么排序后新字符串的第 i 个位置也必须是一个字母
  2. 数字排序规则:

    将原字符串中所有的数字提取出来,统一按升序(0, 1, 2, ..., 9)排列。

  3. 字母排序规则 (自定义):

    将原字符串中所有的字母提取出来,按照一个三级的自定义规则进行排列:

    • 主规则 : 所有小写字母 都排在所有大写字母之前。
    • 次规则 (小写字母内部) : 小写字母之间按字典序升序(a, b, c, ...)。
    • 次规则 (大写字母内部) : 大写字母之间按字典序升序(A, B, C, ...)。
    • 最终的字母排序顺序为: a, b, ..., z, A, B, ..., Z

任务要求

给定一个输入字符串 inputStr,请按照上述规则进行排序,并输出最终的字符串。


输入格式

  • inputStr: 一个字符串。

    • 1 <= inputStr.length < 1000
    • 字符串中仅包含英文字母(a-z, A-Z)和数字(0-9)。

输出格式

  • 一个字符串,表示输入字符串经过排序后的结果。

限制与要求

  • 时间限制 : C/C++ 1000ms, 其他语言 2000ms
  • 内存限制 : C/C++ 64MB, 其他语言 128MB

样例说明

样例 1

  • 输入 : "a2CB1c"

  • 输出 : "a1cB2C"

  • 解释:

    1. 分解与识别:

      • 数字 : 2, 1。它们位于索引 14
      • 字母 : a, C, B, c。它们位于索引 0, 2, 3, 5
      • 位置类型模板为: [字母, 数字, 字母, 字母, 数字, 字母]
    2. 独立排序:

      • 对数字列表 [2, 1] 进行升序排序,得到 [1, 2]
      • 对字母列表 [a, C, B, c] 应用自定义规则排序,得到 [a, c, B, C]
    3. 重构字符串: 将排序后的列表按顺序填充回对应的位置类型。

      • 第1个字母位 (索引0) -> 填充 a
      • 第1个数字位 (索引1) -> 填充 1
      • 第2个字母位 (索引2) -> 填充 c
      • 第3个字母位 (索引3) -> 填充 B
      • 第2个数字位 (索引4) -> 填充 2
      • 第4个字母位 (索引5) -> 填充 C
    4. 最终结果 : 组合起来得到 "a1cB2C"

样例 2

  • 输入 : "ab12C4Ac3B"

  • 输出 : "ab12c3AB4C"

  • 解释:

    1. 分解与识别:

      • 数字 : 1, 2, 4, 3
      • 字母 : a, b, C, A, c, B
    2. 独立排序:

      • 数字排序后: [1, 2, 3, 4]
      • 字母排序后: [a, b, c, A, B, C]
    3. 重构字符串 : 将排序结果依次填入原字符串的字母和数字"插槽"中,得到 "ab12c3AB4C"

java 复制代码
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Scanner;

/**
 * 解决"字符排序"问题的方案类。
 */
public class Solution {
    /**
     * 对包含字母和数字的字符串按特定规则进行排序。
     *
     * 算法思路:
     * 1.  **分离**: 遍历输入字符串一次,将所有数字字符和字母字符分别提取到两个独立的列表中。同时,使用一个布尔数组记录每个原始位置的字符类型(是数字还是字母)。
     * 2.  **排序**:
     * - 对数字列表进行标准的升序排序。
     * - 对字母列表使用一个自定义的比较器 (Comparator)进行排序,以满足"小写字母优先、同类字母按字典序"的规则。
     * 3.  **重构**: 创建一个新的 `StringBuilder`。再次遍历原始字符串的长度(从 0 到 n-1),根据布尔数组中记录的位置类型,依次从排序好的数字列表或字母列表中取出元素,填充到 `StringBuilder` 中。
     *
     * @param inputStr 待排序的输入字符串。
     * @return 排序后的新字符串。
     */
    public String sortString(String inputStr) {
        // --- 1. 处理边界情况 ---
        if (inputStr == null || inputStr.isEmpty()) {
            return ""; // 如果输入为空或 null,返回空字符串
        }

        int n = inputStr.length();

        // --- 2. 分离数字和字母,并记录位置类型 ---
        List<Character> letters = new ArrayList<>();   // 用于存储所有字母
        List<Character> digits = new ArrayList<>();    // 用于存储所有数字
        boolean[] isPositionDigit = new boolean[n]; // 标记每个位置的原始类型 (true:数字, false:字母)

        // 遍历原始字符串,进行分离
        for (int i = 0; i < n; i++) {
            char c = inputStr.charAt(i);
            if (Character.isDigit(c)) {
                digits.add(c);
                isPositionDigit[i] = true; // 标记此位置为数字
            } else { // 题目保证只有字母和数字
                letters.add(c);
                isPositionDigit[i] = false; // 标记此位置为字母
            }
        }

        // --- 3. 对分离出的列表进行排序 ---

        // a. 对数字列表进行升序排序。
        //    对于 '0'-'9' 字符,其字典序与数值大小顺序一致。
        Collections.sort(digits);

        // b. 对字母列表按自定义规则排序。
        //    规则:小写字母 > 大写字母;同为小写或同为大写时,按字典序。
        //    即排序结果为 a, b, ..., z, A, B, ..., Z
        letters.sort((c1, c2) -> {
            boolean c1IsLower = Character.isLowerCase(c1);
            boolean c2IsLower = Character.isLowerCase(c2);

            // 如果一个是小写,另一个是大写
            if (c1IsLower && !c2IsLower) {
                return -1; // 小写字母 c1 排在前面
            }
            if (!c1IsLower && c2IsLower) {
                return 1;  // 大写字母 c1 排在后面
            }

            // 如果两者同为小写或同为大写,则按标准的字典序 (a-z, A-Z) 排序
            return Character.compare(c1, c2);
        });

        // --- 4. 重构最终字符串 ---
        // 使用 StringBuilder 高效构建字符串
        StringBuilder resultBuilder = new StringBuilder(n);
        int digitPtr = 0;  // 指向已排序数字列表的当前位置
        int letterPtr = 0; // 指向已排序字母列表的当前位置

        // 遍历原始字符串的每个位置
        for (int i = 0; i < n; i++) {
            // 检查该位置原始的类型
            if (isPositionDigit[i]) {
                // 如果原来是数字,从排序好的数字列表中取一个,并追加到结果
                resultBuilder.append(digits.get(digitPtr));
                digitPtr++; // 指针后移
            } else {
                // 如果原来是字母,从排序好的字母列表中取一个,并追加到结果
                resultBuilder.append(letters.get(letterPtr));
                letterPtr++; // 指针后移
            }
        }

        // --- 5. 返回结果 ---
        return resultBuilder.toString();
    }
}

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String inputStr = scanner.nextLine(); // 读取输入字符串
        scanner.close();

        // 创建 Solution 类的实例
        Solution solution = new Solution();
        // 调用核心方法
        String result = solution.sortString(inputStr);

        // 输出结果
        System.out.println(result);
    }
}
相关推荐
JavaGuide13 分钟前
感谢数字马力收留,再也不想面试了!!
java·后端
望获linux20 分钟前
【Linux基础知识系列】第五十四篇 - 网络协议基础:TCP/IP
java·linux·服务器·开发语言·架构·操作系统·嵌入式软件
刚入坑的新人编程22 分钟前
暑期算法训练.3
c++·算法
liupenglove25 分钟前
云端【多维度限流】技术方案设计,为服务稳定保驾护航
java·开发语言·网络
平哥努力学习ing34 分钟前
C语言内存函数
c语言·开发语言·算法
weixin_4196583141 分钟前
数据结构之B-树
java·数据结构·b树
H_HX_xL_L42 分钟前
数据结构的算法分析与线性表<1>
数据结构·算法
xienda43 分钟前
数据结构排序算法总结(C语言实现)
数据结构·算法·排序算法
科大饭桶43 分钟前
数据结构自学Day8: 堆的排序以及TopK问题
数据结构·c++·算法·leetcode·二叉树·c