[NOIP 2011 普及组] 统计单词数
在文章两端补空格后查找带空格的目标单词,从而实现不区分大小写的整词匹配。
OJ: luogu
题目 ID: P1308
难度:普及-
标签:字符串模拟python
日期: 2026-06-19 10:13
题意
给定一个目标单词和一整行文章。匹配时不区分大小写,但必须匹配完整单词,不能只匹配某个长单词的一部分。输出出现次数和第一次出现的位置;如果没有出现,输出 -1。
思路
先把目标单词和文章都转成小写。
为了保证“完整单词”匹配,可以在文章两端各补一个空格,并把目标单词也变成 " " + word + " "。这样只有左右都是边界空格时才会匹配。
如果:
text
padded_article = " " + article + " "
padded_word = " " + word + " "那么 padded_article.find(padded_word) 返回的位置,刚好等于目标单词在原文章中的起始位置。
这题是整行输入和字符串查找练习,不创建 brute.py。
Python 知识
/home/rainboy/mycode/hugo-blog/content/program_language/python/oj_input_output_cheatsheet.md:文章包含空格,需要整行读取,不能用split()丢掉空格位置。/home/rainboy/mycode/hugo-blog/content/program_language/python/input_output_and_strings.md:lower()、find()、count()是常用字符串操作。rstrip("\n")只删除行末换行,保留文章中的空格。find()找不到返回-1。
代码
python
word = input().strip().lower()
article = input().rstrip("\n").lower()
padded_article = " " + article + " "
padded_word = " " + word + " "
first_position = padded_article.find(padded_word)
if first_position == -1:
print(-1)
else:
print(padded_article.count(padded_word), first_position)Guide 风格代码
cppbook《C++ 快速入门》教学风格的写法(std:: 前缀、i += 1 循环、0 起始下标):
cpp
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-08-14 15:18
* update_at: 2026-08-14 15:18
*/
/* P1308 统计单词数:整行读入,转成小写后按空格切出单词,完整匹配才计数。 */
#include <iostream>
#include <string>
// 大写字母转小写,其他字符原样返回
char to_lower_char(char ch) {
if (ch >= 'A' && ch <= 'Z') {
return char(ch - 'A' + 'a');
}
return ch;
}
// 判断 article[left..right-1] 这一段与 word 是否完全相同
bool is_same_word(const std::string& article, int left, int right,
const std::string& word) {
if (right - left != (int)word.size()) {
return false;
}
for (int k = 0; k < right - left; k += 1) {
if (article[left + k] != word[k]) {
return false;
}
}
return true;
}
int main() {
std::string word;
std::string article;
std::getline(std::cin, word); // 文章里有空格,必须整行读取
std::getline(std::cin, article);
// 两个字符串都转成小写,匹配时不区分大小写
for (int i = 0; i < (int)word.size(); i += 1) {
word[i] = to_lower_char(word[i]);
}
for (int i = 0; i < (int)article.size(); i += 1) {
article[i] = to_lower_char(article[i]);
}
int match_count = 0;
int first_pos = -1; // -1 表示还没有出现过
// 逐个切出文章中以空格分隔的单词,left..right-1 是当前单词
int i = 0;
int length = (int)article.size();
while (i < length) {
while (i < length && article[i] == ' ') {
i += 1; // 跳过单词前的空格
}
if (i >= length) {
break; // 后面已经没有单词
}
int left = i; // 单词起始位置,位置从 0 开始
while (i < length && article[i] != ' ') {
i += 1;
}
int right = i;
if (is_same_word(article, left, right, word)) {
match_count += 1;
if (first_pos == -1) {
first_pos = left;
}
}
}
if (match_count == 0) {
std::cout << -1 << '\n';
} else {
std::cout << match_count << ' ' << first_pos << '\n';
}
return 0;
}Pythonic 写法
re.finditer + 单词边界统计出现次数与首位置:
python
import re
word = input().strip()
article = input().rstrip("\n")
matches = list(re.finditer(rf"(?i)\b{re.escape(word)}\b", article))
print(-1 if not matches else f"{len(matches)} {matches[0].start()}")复杂度
设文章长度为 n,字符串查找和计数都是线性级别,时间复杂度为
总结
整词匹配的关键是处理边界。给文章和目标词补空格,可以把“左右是单词边界”的判断转化成普通子串查找。