1. 题目背景与需求分析
这道来自NOIP2011普及组的编程题,考察的是字符串处理的基础能力。题目要求实现一个类似文本编辑器查找功能的小程序,核心需求可以拆解为三个关键点:
- 大小写不敏感匹配:程序需要忽略字母大小写差异进行单词比对
- 完全匹配规则:目标单词必须作为独立单词出现,不能是其他单词的一部分
- 位置统计要求:需要记录首次出现的位置(从0开始计数)和总出现次数
在实际开发中,这类功能常见于文档处理工具(如IDE的代码搜索、办公软件的查找替换),理解其实现原理对培养字符串操作思维很有帮助。
2. 核心算法设计思路
2.1 输入处理要点
首先需要注意题目对输入格式的特殊要求:
- 第一行是纯字母组成的待查找单词
- 第二行是可能包含字母和空格的文章内容
- 空格在位置计算时需要被计入(每个空格占1个位置)
cpp复制string keyword, article;
getline(cin, keyword); // 读取单词
getline(cin, article); // 读取文章
2.2 大小写统一处理
为了实现大小写不敏感的匹配,常规做法是将所有字符统一转为小写(或大写):
cpp复制transform(keyword.begin(), keyword.end(), keyword.begin(), ::tolower);
transform(article.begin(), article.end(), article.begin(), ::tolower);
2.3 单词边界识别
完全匹配的关键在于正确识别单词边界。可以通过以下方法处理:
- 使用空格作为分隔符拆分文章
- 或者遍历字符时记录单词起始和结束位置
推荐第二种方法,因为可以同时计算字符位置:
cpp复制int wordStart = 0;
for(int i = 0; i <= article.length(); i++) {
if(i == article.length() || article[i] == ' ') {
// 此时wordStart到i-1是一个单词
wordStart = i + 1;
}
}
3. 完整实现方案
3.1 基础版本代码
cpp复制#include <iostream>
#include <algorithm>
using namespace std;
int main() {
string keyword, article;
getline(cin, keyword);
getline(cin, article);
// 统一转为小写
transform(keyword.begin(), keyword.end(), keyword.begin(), ::tolower);
transform(article.begin(), article.end(), article.begin(), ::tolower);
int count = 0, firstPos = -1;
int wordStart = 0;
// 在文章末尾添加一个空格方便处理最后一个单词
article += ' ';
for(int i = 0; i < article.length(); i++) {
if(article[i] == ' ') {
if(i > wordStart) { // 确保不是连续空格
string word = article.substr(wordStart, i - wordStart);
if(word == keyword) {
count++;
if(firstPos == -1) {
firstPos = wordStart;
}
}
}
wordStart = i + 1;
}
}
if(count > 0) {
cout << count << " " << firstPos;
} else {
cout << -1;
}
return 0;
}
3.2 性能优化要点
对于文章长度可能达到1e6的情况,需要注意:
- 避免频繁的字符串拷贝(如substr)
- 减少不必要的内存分配
- 使用KMP算法可以优化匹配效率(虽然本题数据规模不需要)
优化后的匹配逻辑:
cpp复制bool isMatch(const string& article, int start, int end, const string& keyword) {
if(end - start != keyword.length()) return false;
for(int i = 0; i < keyword.length(); i++) {
if(article[start + i] != keyword[i]) return false;
}
return true;
}
4. 常见问题与调试技巧
4.1 边界情况处理
实际测试时需要特别注意以下情形:
- 文章开头或结尾有多个连续空格
- 查找单词恰好出现在文章开头或结尾
- 查找单词与文章单词大小写混合
- 文章为空或只包含空格
4.2 调试示例
假设输入为:
code复制To
to be or not to be
正确输出应为:
code复制2 0
调试时可以添加打印语句检查中间结果:
cpp复制cout << "Current word: " << word << " at position " << wordStart << endl;
4.3 易错点分析
- 位置计算错误:忘记空格也占位置,或者从1开始计数
- 大小写处理遗漏:只转换了单词或文章其中一方
- 连续空格处理:多个连续空格导致识别出空单词
- 最后一个单词遗漏:循环结束时未处理最后一个单词
5. 算法扩展思考
虽然本题使用基础字符串操作即可解决,但在实际工程应用中,我们可能会考虑:
- 多关键词搜索:扩展为同时查找多个单词
- 模糊匹配:支持通配符或正则表达式
- 大规模文本处理:使用更高效的字符串搜索算法(如Boyer-Moore)
- 并行处理:对超长文本分块并行处理
对于竞赛准备,建议进一步练习:
- 洛谷P1012 拼数
- 洛谷P1305 新二叉树
- 洛谷P1553 数字反转(升级版)
这些题目都能帮助巩固字符串处理的基本功。在实际编码时,养成先处理特殊情况的习惯,比如空输入、边界值等,可以避免很多不必要的错误。
