拼数详解|字符串拼接与自定义排序技巧(洛谷P1012)

一、问题引入(痛点场景)

真题溯源:NOIP 1998提高组第二题(洛谷P1012)
用户痛点

"为什么直接按数值大小排序不行?如何比较字符串拼接的大小?自定义排序规则怎么设计?"

这是经典的字符串拼接排序问题,在CSP-J/S竞赛中考查选手对字符串操作和自定义排序的理解。题目要求将数字拼接成最大的整数,需要特殊的比较规则。

竞赛价值:此类问题在近年竞赛中出现频率较高,占分约10-15分,是字符串处理的重要题型。

二、核心算法分析

2.1 问题本质:字符串拼接比较

关键洞察

  1. 不能简单按数值大小排序(如:12和121,12112 < 12121)
  2. 需要比较两种拼接方式:a+b 与 b+a
  3. 选择拼接后字典序更大的组合

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 同类题型推荐

  1. 洛谷P1107:最大整数(类似拼接问题)
  2. 洛谷P1786:帮贡排序(多关键字排序)
  3. 洛谷P1013:进制位(NOIP1998同场题目)

7.2 算法思维拓展

从拼数问题到更复杂问题

  • 掌握字符串比较的特殊规则设计
  • 理解自定义排序的通用模式
  • 学会处理数字与字符串的转换

竞赛技巧提升

  • 熟练使用STL的sort函数与Lambda表达式
  • 掌握字符串拼接的高效方法
  • 注意边界情况和特殊输入的处理

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

杨小码不BUG

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值