news 2026/6/26 14:42:04

信息学奥赛刷题笔记:OpenJudge NOI 1.7 27 单词翻转的三种解法(C++实现)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
信息学奥赛刷题笔记:OpenJudge NOI 1.7 27 单词翻转的三种解法(C++实现)

信息学奥赛刷题笔记:OpenJudge NOI 1.7 27 单词翻转的三种解法(C++实现)

在信息学奥赛(NOI)的备赛过程中,字符串处理是基础但至关重要的技能。OpenJudge平台上的NOI 1.7 27题"单词翻转"就是一个经典的字符串操作题目,要求将输入句子中的每个单词进行翻转,但保持单词间的原始顺序。这道题目看似简单,却能考察选手对字符串处理、数组操作和算法思维的掌握程度。本文将深入分析三种不同的C++实现方法,从最基础的二维数组到更高级的string类操作,再到直接遍历的高效解法,帮助备赛的OIer们理解不同解法的优劣,提升解题的灵活性和代码优化能力。

1. 解法一:二维数组保存与翻转

二维数组是C语言风格字符串处理的典型代表,适合对内存操作有严格要求或需要兼容旧代码库的场景。这种方法的思路清晰:先将整个句子拆分成独立的单词存入二维数组,然后逐个翻转每个单词,最后输出结果。

#include <bits/stdc++.h> using namespace std; void rev(char s[]) { int len = strlen(s); for(int i = 0; i < len / 2; ++i) swap(s[i], s[len-1-i]); } int main() { char s[505], w[500][505]; cin.getline(s, 505); int len = strlen(s), wi = 0, wj = 0; for(int i = 0; i <= len; ++i) { if(s[i] == ' ' || s[i] == '\0') { w[wi++][wj] = '\0'; wj = 0; } else { w[wi][wj++] = s[i]; } } for(int i = 0; i < wi; ++i) { rev(w[i]); cout << w[i] << ' '; } return 0; }

关键点分析:

  • 内存预分配:需要预先估计最大单词数量和单词最大长度(这里分别设为500和505)
  • 手动字符串终止:每个单词后需要手动添加'\0'作为结束符
  • 双重索引管理:wi记录单词数量,wj记录当前单词的字符位置
  • 自定义翻转函数:rev()函数通过交换首尾字符实现翻转

注意:在本地测试时,控制台输入后需要按Ctrl+Z再回车来模拟EOF结束输入

2. 解法二:string类与STL算法

C++的string类提供了更高级、更安全的字符串操作方式,配合STL算法可以大幅简化代码。这种方法利用了string类的substr方法和STL的reverse算法,代码更加简洁易读。

#include <bits/stdc++.h> using namespace std; int main() { string s, w[500]; int wi = 0, b = 0; getline(cin, s); for(int i = 0; i <= s.length(); ++i) { if(s[i] == ' ' || s[i] == '\0') { w[wi++] = s.substr(b, i-b); b = i+1; } } for(int i = 0; i < wi; ++i) { reverse(w[i].begin(), w[i].end()); cout << w[i] << ' '; } return 0; }

优势对比:

特性二维数组解法string类解法
内存管理手动预分配自动动态分配
字符串操作手动处理内置方法
代码复杂度较高较低
安全性易出现越界相对安全
适用场景嵌入式/C兼容现代C++项目

3. 解法三:直接遍历与即时输出

前两种方法都需要额外存储所有单词,而直接遍历法则采用"边读取边处理"的策略,无需存储中间结果,显著降低了空间复杂度,特别适合处理超长字符串。

#include <bits/stdc++.h> using namespace std; int main() { char s[505]; cin.getline(s, 505); int b = 0, len = strlen(s); for(int i = 0; i <= len; ++i) { if(s[i] == ' ' || s[i] == '\0') { for(int j = i - 1; j >= b; j--) cout << s[j]; cout << ' '; b = i + 1; } } return 0; }

性能对比分析:

  1. 空间复杂度

    • 二维数组:O(n*m),n为单词数,m为最长单词长度
    • string类:O(n*m),但动态分配更灵活
    • 直接遍历:O(1),仅需存储原始字符串
  2. 时间复杂度

    • 三种方法均为O(L),L为字符串总长度
    • 直接遍历法减少了中间存储步骤,实际运行可能更快
  3. 适用场景选择

    • 需要后续处理单词:选择前两种
    • 仅需输出结果:优先考虑直接遍历
    • 内存严格受限:直接遍历最优

4. 解题技巧与常见错误

在实际编程竞赛中,字符串处理题目往往隐藏着一些陷阱。以下是解决单词翻转类题目时的实用技巧和常见错误:

输入处理技巧:

  • 使用getline而非cin >>读取整行,避免被空格截断
  • 注意字符串末尾的'\0'处理
  • 本地测试时使用Ctrl+Z(Windows)或Ctrl+D(Linux/Mac)模拟EOF

边界条件检查:

  • 空字符串输入
  • 仅有一个单词的情况
  • 单词间多个连续空格
  • 字符串开头或结尾有空格

常见错误示例:

// 错误1:忘记处理字符串结束符 for(int i = 0; i < len; ++i) { // 应为i <= len if(s[i] == ' ') { // ... } } // 错误2:翻转时索引计算错误 void rev(char s[]) { int len = strlen(s); for(int i = 0; i < len; ++i) // 应为i < len/2 swap(s[i], s[len-i]); // 应为s[len-1-i] }

优化建议:

  1. 对于固定字符集(如仅小写字母),可考虑位操作优化
  2. 在性能关键场景,直接指针操作可能比数组索引更快
  3. 使用reserve()预分配string内存减少重分配开销
  4. 考虑使用stringstream进行更灵活的单词分割

5. 扩展思考与变式题目

掌握基础解法后,可以通过以下变式题目进一步巩固字符串处理能力:

变式1:保留标点位置输入可能包含标点符号,要求翻转单词但保持标点位置不变。例如: "hello, world!" → "olleh, dlrow!"

变式2:指定单词翻转仅翻转句子中特定位置的单词(如偶数位置的单词)

变式3:多语言支持处理包含多字节字符(如中文)的文本翻转

变式4:内存极致优化在严格内存限制下(如1MB),处理超长文本(如1GB)的单词翻转

// 变式1的参考解法框架 void reverseWordsKeepPunctuation(string &s) { vector<pair<string, bool>> tokens; // string和是否为单词的标志 // 分词逻辑... for(auto &token : tokens) { if(token.second) // 如果是单词则翻转 reverse(token.first.begin(), token.first.end()); } // 重组字符串... }

在实际比赛中,选择哪种方法取决于具体问题的约束条件和个人的编码习惯。二维数组解法虽然略显原始,但在某些内存受限的嵌入式环境中可能是唯一选择;string类解法代码简洁,适合快速开发;直接遍历法则在空间效率上无可匹敌。理解每种方法的优劣,才能在比赛中根据题目特点做出最佳选择。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/14 5:54:12

相关性特征选择不是黑箱:Pearson/Spearman/Kendall 原理与工程实践

1. 项目概述&#xff1a;为什么 correlation feature selection 不该是“黑箱操作”在真实的数据科学项目里&#xff0c;我见过太多人把特征选择当成一个“点一下就出结果”的魔法按钮。刚入行那会儿&#xff0c;我也这么干过——拿到数据集&#xff0c;直接扔进SelectKBest或者…

作者头像 李华
网站建设 2026/6/24 22:48:58

PinMe全栈升级:一句话搞定前端+后端+数据库+部署

过去做一个带登录、邮件验证、数据库、AI调用的应用&#xff0c;光把基础设施搭起来就要大半天——Vercel部署、服务器、Supabase申请数据库、Resend发验证码……每一项单独注册配置。PinMe 这次的更新把这些全包了。官网首屏换成了「一条命令&#xff0c;搭起你的 Web 应用」&…

作者头像 李华
网站建设 2026/6/26 14:41:24

3分钟搞定!Windows包管理器Winget一键安装终极指南

3分钟搞定&#xff01;Windows包管理器Winget一键安装终极指南 【免费下载链接】winget-install Install WinGet using PowerShell! Prerequisites automatically installed. Works on Windows 10/11 and Server 2019/2022. 项目地址: https://gitcode.com/gh_mirrors/wi/win…

作者头像 李华