牛客网4812题:手把手教你实现保留非字母位置的字符串排序

一、问题重述
仅对字母字符进行排序(a-z, A-Z)
排序时不区分大小写(即"A"和"a"视为相同)
当同一字母的大小写同时存在时,保持它们在原字符串中的相对顺序
非字母字符(如数字、标点、空格等)保持原位不变
二、算法设计思路
1. 数据收集阶段
首先,我们需要遍历原始字符串,收集所有字母字符及其在字符串中的位置信息。这里使用vector<pair<char, int>>结构存储,其中:
char保存字母本身int保存该字母在原字符串中的索引位置
2. 自定义排序阶段
使用标准库的sort函数配合自定义比较器:
将字母统一转换为小写进行比较
如果小写形式相同,则比较它们在原字符串中的位置,保持输入顺序
3. 字符串重构阶段
再次遍历原始字符串:
遇到字母字符时,从已排序的字母列表中按顺序取出字符替换
遇到非字母字符时,保持原样不变
三、关键代码解析
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
// 自定义比较函数,用于字母排序
bool compare(const pair<char, int>& a, const pair<char, int>& b) {
// 转换为小写后比较
char lowerA = tolower(a.first);
char lowerB = tolower(b.first);
if (lowerA != lowerB) {
return lowerA < lowerB;
}
// 相同字母保持输入顺序
return a.second < b.second;
}
string sortString(string s) {
vector<pair<char, int>> letters; // 存储字母及其原始位置
// 第一步:收集所有字母并记录位置
for (int i = 0; i < s.size(); i++) {
if (isalpha(s[i])) {
letters.emplace_back(s[i], i);
}
}
// 第二步:对字母进行排序
sort(letters.begin(), letters.end(), compare);
// 第三步:重构字符串
int letterIndex = 0;
for (int i = 0; i < s.size(); i++) {
if (isalpha(s[i])) {
s[i] = letters[letterIndex].first;
letterIndex++;
}
}
return s;
}
int main() {
string input;
getline(cin, input); // 读取整行输入
string result = sortString(input);
cout << result << endl;
return 0;
}四、常见错误与调试技巧
忘记处理大小写:确保使用
tolower或toupper统一字母形式顺序保持错误:比较函数中必须包含原始位置比较
边界条件:测试空字符串、全非字母字符串、全大写/全小写字符串等情况
五、扩展思考
可以尝试改进算法:
处理Unicode字符而不仅限于ASCII
添加更多排序规则(如数字的特殊处理)
优化空间复杂度,尝试原地排序
六、总结
通过本文,我们学习了一个实用的字符串处理技巧:如何在保留非字母字符位置的同时,对字母进行不区分大小写的排序。掌握这种分离处理的思想,可以解决许多类似的复杂字符串处理问题。
原创内容 转载请注明出处
