第一版(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")

  1. 第一版最后输出的时候,引入了一个错误的判定。
  2. 本意是想判定当下要替换的s[i]是不是字母,结果变成了判定「是不是A/a,是不是B/b,……,是不是Z/z」。
  3. 目前还无法总结错误原因,但是可以知道,如果只要判断一个字符是不是字母,那么直接用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")

  1. 我们第一版的提取逻辑是对的,第二版我们改一下替换逻辑就可以。
  2. 具体的改法就是去掉复杂且容易出错的字母判断逻辑,改为用简单的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")

  1. 注意遍历字符串时char c : schar& c : s的区别,前者是复制,后者是引用。
  2. 前者是复制,后续对复制的副本操作,适用于「只对字符串进行读操作」。
  3. 后者是引用,后续对原字符串操作,适用于需要修改的情况。
  4. 这个代码把「按顺序提取」「稳定排序」和「替换」分成三步完成,而第二版把「按顺序提取」和「稳定排序」合成一步完成。
  5. 稳定排序函数可以配合自定义的lambda比较函数,便于完成这种大小写字母挨在一起,整体按顺序排的情况,也就是大小写不敏感的字符串排序。
  6. 3里讲的技巧还有以下常见应用——
  7. 分离奇偶的同时保持相对顺序。
  8. 多条件排序,比如先按年龄再按姓名排序。
  9. 按字符串长度排序。
  10. ambda函数的返回值如果是1,则将按先a后b的顺序排序,如果是0,则会保持不变。我们写的表达式并不完善,但C++帮助我们完成了二次比较。