从OpenJudge到NOI:C++字符串空格过滤的深度优化与实战心法
在信息学奥赛的赛场上,字符串处理是绕不开的基础课题。无论是OpenJudge上的日常练习,还是NOI(全国青少年信息学奥林匹克竞赛)的正式赛场,一道看似简单的“过滤多余空格”题目,往往能拉开选手之间的差距。这不仅仅是关于写出一个能通过测试的程序,更是关于如何在有限的内存和时间内,写出优雅、高效、鲁棒的代码。很多初学者会满足于一种解法,但真正的竞赛选手会像打磨艺术品一样,从多个维度审视同一个问题:字符数组与std::string的性能博弈、原地修改与新建字符串的空间取舍、以及面对不确定输入时的稳健处理策略。今天,我们就深入这个“小”问题,拆解其背后的大世界,分享一套从基础实现到竞赛级优化的完整心法。
1. 问题本质与竞赛场景分析
在OpenJudge NOI 1.7-23这类题目中,“过滤多余空格”的核心要求是:将输入字符串中连续的多个空格压缩为单个空格,同时保留单词之间最初的那个空格,并确保字符串首尾没有多余空格(通常题目隐含此要求)。这听起来简单,但在竞赛的高压环境下,我们需要快速、准确地洞悉其背后的几个关键点:
- 输入边界的不确定性:题目可能给出单行字符串(用
getline读取),也可能输入是多行、不定数量的单词流(需要while(cin >> str)循环读取)。前者考验对字符串的精细遍历,后者则考验对输入流终止条件(EOF)的理解和操作。 - 性能的隐性要求:虽然一道题目的测试数据可能不大,但养成关注性能的习惯至关重要。在更复杂的赛题中,字符串处理可能只是其中一环,低效的实现会成为整个算法的瓶颈。我们需要思考:是使用C风格字符数组进行底层操作更快,还是利用
std::string的便利性更划算?内存拷贝的次数是否可以减少? - 代码的简洁性与可读性:在分秒必争的比赛中,代码不仅要快,还要写得快,且不易出错。一个逻辑清晰、行数简洁的解法,能为你节省宝贵的调试时间。
我们先来看一个最直观,但也最值得深究的解法:遍历并构造新字符串。
2. 基础解法剖析:遍历与构造的艺术
这种思路是创建一个新的字符串(或字符数组),遍历原字符串,根据空格连续出现的规则,选择性地将字符追加到新字符串中。
2.1 使用字符数组(C风格字符串)
#include <cstdio>
#include <cstring>
int main() {
char input[1005];
char output[1005];
fgets(input, 1005, stdin); // 使用fgets安全读取,包含可能的换行符
int input_len = strlen(input);
int output_idx = 0;
int space_count = 0;
for (int i = 0; i < input_len; ++i) {
if (input[i] == ' ' || input[i] == '\n') { // 同时处理空格和结尾换行
space_count++;
if (space_count == 1) {
output[output_idx++] = ' '; // 只保留第一个空格
}
} else {
output[output_idx++] = input[i];
space_count = 0; // 遇到非空格字符,重置空格计数器
}
}
// 处理末尾可能被添加的空格
if (output_idx > 0 && output[output_idx - 1] == ' ') {
output_idx--;
}
output[output_idx] = '\0'; // 别忘了C风格字符串的结束符!
printf("%s\n", output);
return 0;
}
注意:使用字符数组时,必须手动管理内存和结束符
\0。fgets会读入换行符,所以在判断时需要考虑。同时,循环结束后检查并移除末尾可能多余的空


328

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



