第一版(0/22)
#include <algorithm>
#include <cctype>
#include <iostream>
#include <vector>
using namespace std;
int main() {
string s;
getline(cin, s);
string n = "";
for (int j = 0; j < 26; j++) {
for (int i = 0; i < s.size(); i++) {
if (s[i] - 'A' == j || s[i] - 'a' == j) {
n.push_back(s[i]);
}
}
}
int count = 0;
for (int i = 0; i < s.size(); i++) {
for (int j = 0; j < 26; j++) {
if (s[i] - 'A' == j || s[i] - 'a' == j) {
s[i] = n[count];
count++;
}
}
}
cout << s;
}
// 64 位输出请用 printf("%lld")
- 第一版最后输出的时候,引入了一个错误的判定。
- 本意是想判定当下要替换的
s[i]
是不是字母,结果变成了判定「是不是A/a,是不是B/b,……,是不是Z/z」。 - 目前还无法总结错误原因,但是可以知道,如果只要判断一个字符是不是字母,那么直接用
isalpha()
是最好的选择。
第二版(AC)
#include <algorithm>
#include <cctype>
#include <iostream>
#include <vector>
using namespace std;
int main() {
string s;
getline(cin, s);
string n = "";
for (int j = 0; j < 26; j++) {
for (int i = 0; i < s.size(); i++) {
if (s[i] - 'A' == j || s[i] - 'a' == j) {
n.push_back(s[i]);
}
}
}
int count = 0;
for (int i = 0; i < s.size(); i++) {
if (isalpha(s[i])) {
s[i] = n[count];
count++;
}
}
cout << s;
}
// 64 位输出请用 printf("%lld")
- 我们第一版的提取逻辑是对的,第二版我们改一下替换逻辑就可以。
- 具体的改法就是去掉复杂且容易出错的字母判断逻辑,改为用简单的
isalpha()
函数。
第三版(稳定排序)
#include <algorithm>
#include <cctype>
#include <iostream>
#include <vector>
using namespace std;
int main() {
string s;
getline(cin, s);
string n = "";
for (char c : s) {
if (isalpha(c)) {
n.push_back(c);
}
}
stable_sort(n.begin(), n.end(), [](char a, char b){
return toupper(a) < toupper(b);
});
int count = 0;
for (char& c : s) {
if (isalpha(c)) {
c = n[count];
count++;
}
}
cout << s;
}
// 64 位输出请用 printf("%lld")
- 注意遍历字符串时
char c : s
和char& c : s
的区别,前者是复制,后者是引用。 - 前者是复制,后续对复制的副本操作,适用于「只对字符串进行读操作」。
- 后者是引用,后续对原字符串操作,适用于需要修改的情况。
- 这个代码把「按顺序提取」「稳定排序」和「替换」分成三步完成,而第二版把「按顺序提取」和「稳定排序」合成一步完成。
- 稳定排序函数可以配合自定义的lambda比较函数,便于完成这种大小写字母挨在一起,整体按顺序排的情况,也就是大小写不敏感的字符串排序。
- 3里讲的技巧还有以下常见应用——
- 分离奇偶的同时保持相对顺序。
- 多条件排序,比如先按年龄再按姓名排序。
- 按字符串长度排序。
- ambda函数的返回值如果是1,则将按先a后b的顺序排序,如果是0,则会保持不变。我们写的表达式并不完善,但C++帮助我们完成了二次比较。