1. 题目解析与需求拆解
今天我们来拆解一道来自华为OD机考的字符串处理题目。这道题看似简单,但实际考察了多种字符串操作和排序逻辑的综合运用能力。作为经历过多次机考的老手,我发现这类题目往往在边界条件和排序规则上设置陷阱,需要格外小心。
题目要求我们对给定字符串进行两步处理:
- 对每个单词内部字符按字典序重新排列
- 对所有单词按特定规则重新排序
输入约束条件:
- 字符范围:大小写字母、数字和空格
- 字符串长度:1-1000个字符
- 输出要求:单词间单空格分隔,首尾无空格
注意:题目中的"字典序"指的是ASCII码顺序,即数字<大写字母<小写字母。例如"aB1"排序后应为"1Ba"
2. 核心算法设计与实现
2.1 单词内部排序实现
首先我们需要将字符串按空格分割成单词列表,然后对每个单词进行内部字符排序。这里有几个技术要点:
def sort_word(word): # 将单词转为字符列表并排序 return ''.join(sorted(word))关键细节:
sorted()函数默认按ASCII码升序排列- 数字0-9的ASCII码是48-57
- 大写字母A-Z是65-90
- 小写字母a-z是97-122
2.2 单词统计与排序规则
这部分是本题的核心难点,需要实现三级排序规则:
- 主排序:按单词出现频率降序
- 次级排序:频率相同时按单词长度升序
- 三级排序:前两者都相同时按字典序升序
from collections import defaultdict def process_string(s): # 分割字符串并处理每个单词 words = [sort_word(w) for w in s.split()] # 统计词频 freq = defaultdict(int) for w in words: freq[w] += 1 # 实现三级排序 sorted_words = sorted(words, key=lambda w: (-freq[w], len(w), w)) # 去重并保持顺序 seen = set() result = [] for w in sorted_words: if w not in seen: result.extend([w] * freq[w]) seen.add(w) return ' '.join(result)算法复杂度分析:
- 时间复杂度:O(n*m log m) + O(n log n),其中n是单词数,m是平均单词长度
- 空间复杂度:O(n)用于存储词频和结果
3. 边界条件与特殊测试用例
在实际编码中,我发现以下几个边界情况需要特别注意:
- 全相同单词:如输入"a a a",输出应为"a a a"
- 大小写敏感:"Ab"和"ab"视为不同单词
- 数字与字母混合:"a1"排序后应为"1a"
- 单字符单词:如输入"a b c b",输出应为"b b a c"
- 前导/后缀空格:虽然题目说明用空格分隔,但最好先strip()
测试用例表:
| 输入 | 预期输出 | 说明 |
|---|---|---|
| "hello world" | "ehllo dlorw" | 基础用例 |
| "a A b B a" | "a a A B b" | 大小写敏感 |
| "123 321 123" | "123 123 123" | 数字处理 |
| "tree loves coding" | "eert celov cdgino" | 多单词场景 |
4. 性能优化与实用技巧
4.1 使用生成器减少内存占用
对于大字符串,可以改用生成器表达式:
words = (sort_word(w) for w in s.strip().split())4.2 合并相同单词的排序
观察到相同单词会被多次排序,可以优化:
unique_words = set(words) sorted_unique = sorted(unique_words, key=lambda w: (-freq[w], len(w), w))4.3 使用Counter替代defaultdict
Python的collections.Counter更简洁:
from collections import Counter freq = Counter(words)5. 完整实现与测试
最终优化后的完整解决方案:
from collections import Counter def string_reorder(s): def sort_word(w): return ''.join(sorted(w)) words = [sort_word(w) for w in s.strip().split()] freq = Counter(words) # 获取去重单词并按规则排序 unique_words = sorted(freq.keys(), key=lambda w: (-freq[w], len(w), w)) # 重建结果列表 result = [] for w in unique_words: result.extend([w] * freq[w]) return ' '.join(result) # 测试用例 test_cases = [ ("hello world", "ehllo dlorw"), ("a A b B a", "a a A B b"), ("123 321 123", "123 123 123"), ("tree loves coding", "eert celov cdgino"), ("a b c b", "b b a c") ] for input_str, expected in test_cases: assert string_reorder(input_str) == expected6. 常见问题与调试技巧
Q1:为什么我的排序结果不符合预期?A:检查三级排序规则的实现顺序是否正确:
- 先按频率降序
- 再按长度升序
- 最后按字典序升序
Q2:遇到内存不足错误怎么办?A:对于超长字符串:
- 使用生成器替代列表
- 分批处理单词
- 考虑使用更高效的数据结构如Trie
Q3:如何处理带标点的字符串?A:本题明确限定字符范围,但实际开发中应先清洗数据:
import re clean_s = re.sub(r'[^a-zA-Z0-9 ]', '', s)调试技巧:
- 打印中间变量检查处理过程
- 对每个排序阶段单独测试
- 使用pdb设置断点调试
7. 算法扩展与变种思考
这道题目可以有多种变体,考察不同的能力:
- 大小写不敏感版本:
words = [sort_word(w.lower()) for w in s.split()]保留原始顺序的稳定排序: 需要使用enumerate记录原始位置作为最后一级排序键
多分隔符处理:
import re words = [sort_word(w) for w in re.split(r'[\s,;]+', s)]- 并行化处理: 对于超长字符串,可以用multiprocessing并行处理单词
在实际面试中,完成基础实现后,可以主动讨论这些变体问题的解决方案,展示思维广度。我建议平时练习时,对每道题目都思考可能的变体,这样在面试中就能从容应对考官的追问。