
一、问题引入(痛点场景)
真题溯源:NOIP 1998提高组第二题(洛谷P1012)
用户痛点:
"为什么直接按数值大小排序不行?如何比较字符串拼接的大小?自定义排序规则怎么设计?"
这是经典的字符串拼接排序问题,在CSP-J/S竞赛中考查选手对字符串操作和自定义排序的理解。题目要求将数字拼接成最大的整数,需要特殊的比较规则。
竞赛价值:此类问题在近年竞赛中出现频率较高,占分约10-15分,是字符串处理的重要题型。
二、核心算法分析
2.1 问题本质:字符串拼接比较
关键洞察:
- 不能简单按数值大小排序(如:12和121,12112 < 12121)
- 需要比较两种拼接方式:a+b 与 b+a
- 选择拼接后字典序更大的组合

2.2 算法思路详解
核心比较规则: 对于两个字符串a和b,比较a+b与b+a的字典序:
- 如果a+b > b+a,则a应该排在b前面
- 如果a+b < b+a,则b应该排在a前面
三、代码实现详解
3.1 标准解法(字符串比较)
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
using namespace std;
// 自定义比较函数
bool compare(const string &a, const string &b) {
return a + b > b + a; // 比较两种拼接方式
}
int main() {
int n;
cin >> n;
vector<string> nums(n);
for (int i = 0; i < n; i++) {
cin >> nums[i];
}
// 按自定义规则排序
sort(nums.begin(), nums.end(), compare);
// 拼接所有字符串
string result;
for (const string &num : nums) {
result += num;
}
// 处理前导零的特殊情况(虽然题目保证正整数)
if (result[0] == '0' && result.length() > 1) {
// 找到第一个非零字符
size_t pos = result.find_first_not_of('0');
if (pos != string::npos) {
result = result.substr(pos);
} else {
result = "0";
}
}
cout << result << endl;
return 0;
}
3.2 优化解法(Lambda表达式)
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
using namespace std;
int main() {
int n;
cin >> n;
vector<string> nums(n);
for (int i = 0; i < n; i++) {
cin >> nums[i];
}
// 使用Lambda表达式
sort(nums.begin(), nums.end(), [](const string &a, const string &b) {
return a + b > b + a;
});
string result;
for (const auto &num : nums) {
result += num;
}
cout << result << endl;
return 0;
}
四、算法原理深度解析
4.1 为什么不能直接按数值排序?
反例分析:
- 输入:12, 121
- 数值排序:121, 12 → 拼接:12112
- 正确排序:12, 121 → 拼接:12121
- 12121 > 12112,所以数值排序错误
4.2 拼接比较的正确性证明
传递性证明: 如果a+b > b+a且b+c > c+b,则a+c > c+a 这使得排序结果具有确定性,不会出现循环依赖。
五、避坑指南与调试技巧
5.1 常见错误分析
错误1:直接数值排序
// 错误:直接按数值大小排序
sort(nums.begin(), nums.end(), greater<int>());
// 正确:按字符串拼接比较
sort(nums.begin(), nums.end(), [](string a, string b) {
return a + b > b + a;
});
错误2:忽略前导零
// 输入:0, 0, 0
// 错误输出:000
// 正确输出:0
// 需要特殊处理
if (result[0] == '0' && result.length() > 1) {
// 处理前导零
}
5.2 测试用例设计
void testCases() {
// 用例1:题目样例1
// 输入:13, 312, 343 → 输出:34331213
// 用例2:题目样例2
// 输入:7, 13, 4, 246 → 输出:7424613
// 用例3:边界测试
// 输入:1, 10, 100 → 输出:110100
// 用例4:相同数字前缀
// 输入:12, 121 → 输出:12121
// 用例5:包含零
// 输入:0, 1, 10 → 输出:1100
}
六、性能优化建议
6.1 数据范围分析
题目限制:
- 1 ≤ n ≤ 20(数据量很小)
- 1 ≤ ai ≤ 10^9(数字最多10位)
- 使用O(n²)排序也可接受
6.2 内存优化
避免不必要的字符串拷贝:
// 使用引用传递,避免拷贝
bool compare(const string &a, const string &b) {
return a + b > b + a;
}
七、竞赛应用与扩展
7.1 同类题型推荐
- 洛谷P1107:最大整数(类似拼接问题)
- 洛谷P1786:帮贡排序(多关键字排序)
- 洛谷P1013:进制位(NOIP1998同场题目)
7.2 算法思维拓展
从拼数问题到更复杂问题:
- 掌握字符串比较的特殊规则设计
- 理解自定义排序的通用模式
- 学会处理数字与字符串的转换
竞赛技巧提升:
- 熟练使用STL的sort函数与Lambda表达式
- 掌握字符串拼接的高效方法
- 注意边界情况和特殊输入的处理
&spm=1001.2101.3001.5002&articleId=153650177&d=1&t=3&u=e9ca68203c4343a3a7a4ec54eae277d8)
959

被折叠的 条评论
为什么被折叠?



