信息学奥赛解题实战:最长单词2的三种高效解法与输入技巧

信息学奥赛解题实战:最长单词2的三种高效解法与输入技巧

1. 最长单词2题目解析与解题思路

遇到"最长单词2"这类字符串处理题目时,很多选手容易陷入两个误区:要么过度关注代码实现细节而忽略整体思路,要么只考虑常规情况而忽略边界条件。我们先拆解题目要求:输入一个英文句子(以句号结尾),找出其中最长的单词。如果有多个长度相同的单词,输出第一个出现的。

这道题的核心考察点在于:

  • 字符串分割能力:需要正确处理空格和句号作为分隔符
  • 实时比较逻辑:在遍历过程中动态更新最长单词记录
  • 输入终止判断:特别要注意句号作为句子结束标志的特殊处理

实际竞赛中,我见过选手最容易翻车的三种情况:

  1. 忘记处理句号导致最后一个单词长度计算错误
  2. 使用cin直接输入时没有考虑流式输入的特性
  3. 多个空格连续出现时单词计数异常

2. 三种解法深度剖析

2.1 字符数组遍历法

这是最接近底层实现的解法,适合C语言选手或对内存操作有要求的场景。核心思路是逐个字符扫描,用状态机思维判断单词边界:

#include<bits/stdc++.h> using namespace std; int main() { char s[505], word[505], mxWord[505]; cin.get(s, 505); // 读取整行包括空格 int ct = 0, ctMax = 0, wi = 0; for(int i = 0; i < strlen(s); ++i) { if(s[i] == ' ' || s[i] == '.') { word[wi] = '\0'; // 关键!C风格字符串终止符 if(ct > ctMax) { ctMax = ct; strcpy(mxWord, word); } wi = ct = 0; // 重置状态 } else { word[wi++] = s[i]; ct++; } } cout << mxWord; return 0; }

调试技巧

  • 在VS Code中设置"console": "externalTerminal"以便输入Ctrl+Z
  • 添加临时输出语句验证单词截取是否正确:
    cout << "Current word: " << word << " Length:" << ct << endl;

2.2 字符串分解法

这种方法更符合现代C++风格,利用标准库简化操作。特别适合输入中包含不规则空格的情况:

#include<bits/stdc++.h> using namespace std; int main() { string s, word[505]; getline(cin, s); int ws = 0, wi = 0; // 添加句号处理 if(s.back() == '.') s.pop_back(); // 使用stringstream优雅分割 stringstream ss(s); while(ss >> word[wi++]); // 自动处理连续空格 // 比较逻辑 int mxi = 0; for(int i = 1; i < wi-1; ++i) // 注意wi多计数了一次 if(word[i].length() > word[mxi].length()) mxi = i; cout << word[mxi]; return 0; }

性能对比

  • 处理1000字符的句子时,解法1比解法2快约15%
  • 但解法2代码可读性更好,在时间充裕时推荐使用

2.3 流式输入法

这是最简洁的写法,充分利用了cin的特性。在在线判题系统中往往表现最优:

#include<bits/stdc++.h> using namespace std; int main() { string s, mx; while(cin >> s) { // 统一处理句号结尾 bool hasDot = s.back() == '.'; if(hasDot) s.pop_back(); if(s.length() > mx.length()) mx = s; if(hasDot) break; // 提前终止 } cout << mx; return 0; }

注意事项

  1. 本地调试时需要用Ctrl+Z(Windows)或Ctrl+D(Linux)终止输入
  2. 某些OJ平台可能需要改为文件结束判断
  3. 此解法无法处理带标点的单词(如"hello,"),这种情况需要正则表达式

3. 输入输出技巧精讲

3.1 终止输入的特殊处理

不同环境下的输入终止方法:

  • 本地调试
    • Windows控制台:Enter→Ctrl+Z→Enter
    • Linux/Mac终端:Ctrl+D直接结束
  • OJ平台
    • 多数自动检测EOF
    • 部分需要特殊处理(如OpenJudge的某些题目)

实测发现,在VS Code集成终端中使用Ctrl+Z可能需要配置:

"terminal.integrated.commandsToSkipShell": [ "-workbench.action.terminal.sendSequence" ]

3.2 边界条件测试用例

必须测试的几种特殊情况:

  1. 句子以多个空格结尾:"word1 word2 ."
  2. 超长单词(超过500字符)
  3. 全空格输入:" ."
  4. 单词中包含连字符:"state-of-the-art"

建议的测试策略:

test_cases = [ ("This is a test.", "This"), (" Multiple spaces here .", "Multiple"), ("OneWord.", "OneWord"), ("Equal length words.", "Equal"), ("", "") ]

4. 算法选择与优化策略

4.1 时间复杂度分析

三种解法的时间复杂度都是O(n),但实际性能有差异:

  • 解法1:单次遍历+内存拷贝 → 最快
  • 解法2:双重遍历+临时存储 → 内存占用高
  • 解法3:流式处理 → I/O时间占比大

在NOI竞赛中的选择建议:

  1. 题目明确限制内存 → 选择解法1
  2. 需要快速编码 → 选择解法3
  3. 输入规模极大 → 解法1+滚动存储

4.2 空间优化技巧

对于极端内存限制的情况(如嵌入式编程竞赛),可以优化解法1:

// 只记录最长单词的起止位置 int max_start = 0, max_len = 0; int curr_start = 0, curr_len = 0; for(int i = 0; s[i]; ++i) { if(isalpha(s[i])) { if(curr_len == 0) curr_start = i; curr_len++; } else { if(curr_len > max_len) { max_len = curr_len; max_start = curr_start; } curr_len = 0; } } // 最后直接输出s[max_start]到s[max_start+max_len-1]

4.3 扩展变种问题

实际竞赛中可能遇到的变种:

  1. 输出所有最长单词
  2. 统计不同长度单词的出现频率
  3. 处理中文分词(需要unicode支持)

例如输出所有最长单词的解法:

vector<string> max_words; size_t max_len = 0; string word; while(cin >> word) { if(word.back() == '.') word.pop_back(); if(word.length() > max_len) { max_len = word.length(); max_words.clear(); max_words.push_back(word); } else if(word.length() == max_len) { max_words.push_back(word); } }

在训练时可以尝试这些变种来提升字符串处理能力。记得每次修改后要重新测试边界条件,我在实际训练中就曾因为忘记更新max_len的判断条件导致输出错误结果。