120-字符串字符类型排序问题 | 豆包MarsCode AI刷题

102 阅读4分钟

题目

给定一个字符串,其中仅包含,大小写字母,数字和问号。要求对该字符串内部字符排序,满足以下要求:

  1. 问号的占用的位置不变
  2. 数字占用的位置不变,数字之间由大到小排序
  3. 字母占用的位置不变,字母之间按字典序排序

具体请配合样例理解

样例

输入:

12A?zc

输出:

21A?cz

输入:

1Ad?z?t24

输出:

4Ad?t?z21

输入:

???123??zxy?

输出:

???321??xyz?

思路分析

问题理解

给定一个字符串,其中包含大小写字母、数字和问号。我们需要对这个字符串进行排序,满足以下要求:

  1. 问号的位置不变:问号在排序后仍然保持在原来的位置。
  2. 数字的位置不变,但数字之间按从大到小排序:数字在字符串中的位置保持不变,但同一位置上的数字按从大到小排序。
  3. 字母的位置不变,但字母之间按字典序排序:字母在字符串中的位置保持不变,但同一位置上的字母按字典序排序。

数据结构选择

为了实现上述要求,我们可以使用以下数据结构:

  • List digits:用于存储字符串中的所有数字。
  • List letters:用于存储字符串中的所有字母。
  • char[] result:用于存储最终排序后的字符数组。

算法步骤

  1. 分离字符

    • 遍历输入字符串,将数字、字母和问号分别存储在不同的列表中。

    • 如果当前字符是问号,直接将其放入结果数组中相应的位置。

  2. 排序

    • 对数字列表进行从大到小排序。

    • 对字母列表进行字典序排序。

  3. 重组字符串

    • 再次遍历输入字符串,根据字符类型将排序后的数字和字母插入到结果数组中相应的位置。

具体步骤

  1. 分离字符

    • 遍历输入字符串,将数字、字母和问号分别存储在不同的列表中。

    • 如果当前字符是问号,直接将其放入结果数组中相应的位置。

  2. 排序

    • 对数字列表进行从大到小排序。

    • 对字母列表进行字典序排序。

  3. 重组字符串

    • 再次遍历输入字符串,根据字符类型将排序后的数字和字母插入到结果数组中相应的位置。

代码

import java.util.*;
public class Main {
    public static String solution(String inp) {
        // 分离字符
        List<Character> digits = new ArrayList<>();
        List<Character> letters = new ArrayList<>();
        char[] result = new char[inp.length()];
        
        for (int i = 0; i < inp.length(); i++) {
            char c = inp.charAt(i);
            if (c == '?') {
                result[i] = c;
            } else if (Character.isDigit(c)) {
                digits.add(c);
            } else if (Character.isLetter(c)) {
                letters.add(c);
            }
        }
        
        // 排序数字和字母
        digits.sort((a, b) -> b - a); // 从大到小排序
        letters.sort(Character::compareTo); // 按字典序排序
        
        // 重组字符串
        int digitIndex = 0;
        int letterIndex = 0;
        for (int i = 0; i < inp.length(); i++) {
            if (result[i] == '?') {
                continue;
            } else if (Character.isDigit(inp.charAt(i))) {
                result[i] = digits.get(digitIndex++);
            } else if (Character.isLetter(inp.charAt(i))) {
                result[i] = letters.get(letterIndex++);
            }
        }
        
        return new String(result);
    }

    public static void main(String[] args) {
        // Add your test cases here
        System.out.println(solution("12A?zc").equals("21A?cz"));
        System.out.println(solution("1Ad?z?t24").equals("4Ad?t?z21"));
        System.out.println(solution("???123??zxy?").equals("???321??xyz?"));
    }
}

感悟

我想先讲一下时间复杂度和空间复杂度~时间复杂度是用来衡量算法运行时间随输入规模增长的变化趋势的。

时间复杂度

  1. 遍历字符串:我们需要遍历一次输入字符串,将字符分类并存储到不同的列表中。这个操作的时间复杂度是 O(n),其中 n 是字符串的长度。

  2. 排序:需要对数字和字母列表进行排序。假设数字和字母的总数为 m,排序的时间复杂度通常是 O(m log m)

  3. 重组字符串:再次遍历字符串,将排序后的数字和字母插入到结果数组中。这个操作的时间复杂度也是 O(n)

综合起来,整个算法的时间复杂度主要由排序操作决定,因此总的时间复杂度是 O(n + m log m)

空间复杂度

主要的额外空间开销来自于以下几个部分:

  1. 存储数字和字母的列表:我们需要两个列表来存储数字和字母,这两个列表的总大小是 m。因此,这部分的空间复杂度是 O(m)

  2. 结果数组:我们需要一个与输入字符串长度相同的结果数组来存储最终的排序结果。因此,这部分的空间复杂度是 O(n)

所以整个算法的空间复杂度是 O(n + m)

我个人觉得这道题难度适中,也有很多学到的地方,

比如字符分类:如何根据字符的类型(数字、字母、问号)进行分类和处理。还有字符排序:如何对不同类型的字符进行排序,并保持它们在原字符串中的位置不变。

另外久还有数据结构方面的:List:用于存储和操作字符列表。char[]:用于存储和操作字符数组。都很基础啦~