题解:洛谷 P1321 单词覆盖还原
【题目来源】
【题目描述】
一个长度为 \(l\) 的字符串中被反复贴有 boy 和 girl 两单词,后贴上的可能覆盖已贴上的单词(没有被覆盖的用句点表示),最终每个单词至少有一个字符没有被覆盖。问贴有几个 boy 几个 girl?
【输入】
一行被反复贴有 boy 和 girl 两单词的字符串。
【输出】
两行,两个整数。第一行为 boy 的个数,第二行为 girl 的个数。
【输入样例】
......boyogirlyy......girl.......
【输出样例】
4
2
【核心思想】
-
问题分析:给定一个字符串,其中反复贴有
boy和girl两个单词,后贴上的可能覆盖已贴上的字符(被覆盖的字符用句点.表示)。最终每个单词至少有一个字符没有被覆盖。需要统计贴上的boy和girl的数量。由于覆盖的存在,一个单词可能在字符串中表现为不完整的片段(如b、bo、oy等),但每个单词至少有一个字符可见,且覆盖不会打乱单词内字符的顺序。本质是通过字符片段推断完整单词数量的问题。 -
算法选择:
- 滑动窗口计数:遍历字符串,对于每个位置
i,检查以i开头、长度分别为 3 和 4 的窗口是否包含单词boy或girl的任意字符(因为只要窗口内有单词的字符,就说明这个窗口可能对应一个完整的单词,即使其他字符被覆盖)。这种方法的依据是:每个单词至少有一个字符没有被覆盖,因此在扫描时,如果一个单词的任意一个字符出现在某个位置,就认为这个位置对应一个单词的起始。 - 更精确的贪心(备选):另一种方法是逐个字符统计,确保每个单词的字符被计数一次,但窗口方法在本问题中更常用。
- 滑动窗口计数:遍历字符串,对于每个位置
-
关键步骤:
- 读入:读取字符串
s。 - 统计
boy:遍历i从0到s.length() - 3,如果s[i] == 'b' || s[i+1] == 'o' || s[i+2] == 'y',则boy_count++。这表示在这个长度为 3 的窗口内,至少有一个boy的字符未被覆盖,因此可以认为这里贴了一个boy。 - 统计
girl:遍历i从0到s.length() - 4,如果s[i] == 'g' || s[i+1] == 'i' || s[i+2] == 'r' || s[i+3] == 'l',则girl_count++。 - 输出:分别输出
boy_count和girl_count。
- 读入:读取字符串
-
时间/空间复杂度:
- 时间复杂度:\(O(n)\),只需一次或两次线性扫描。
- 空间复杂度:\(O(1)\),只使用常数变量。
-
覆盖问题的简化:
- 窗口判定的原理:由于每个单词至少有一个字符保留,且保留的字符在单词中的相对位置不变,因此任意一个完整的单词(即使部分被覆盖)在扫描时,其保留的字符会落在某个长度为单词长度的窗口中。通过检查窗口内是否存在单词的任意字符,可以推断出该窗口对应一个单词。
- 重叠处理:这种统计方法可能会对重叠的窗口重复计数,但题目中的覆盖和隐藏关系使得这种计数方式正好对应贴上的单词数量(因为每个单词都会贡献至少一个窗口计数)。
- 适用场景:这类“单词覆盖还原”问题,核心是通过检测字符串中可能存在的单词片段来推断单词总数,是字符串处理中较为巧妙的思维题。
【解题思路】

【算法标签】
入门 #字符串入门
【代码详解】
#include <bits/stdc++.h> // 包含标准库头文件(万能头文件)
using namespace std; // 使用标准命名空间
int main()
{
string s; // 定义字符串:存储输入的字符串
int cb = 0; // 定义变量:统计"boy"出现的次数
int cg = 0; // 定义变量:统计"girl"出现的次数
cin >> s; // 输入字符串
// 统计"boy"出现的次数(检查连续3个字符)
for (int i = 0; i < s.length() - 2; i++)
{
// 检查当前字符、下一个字符、下下个字符是否包含'b'、'o'、'y'
if (s[i] == 'b' || s[i + 1] == 'o' || s[i + 2] == 'y')
{
cb++; // 满足条件则计数加1
}
}
// 统计"girl"出现的次数(检查连续4个字符)
for (int i = 0; i < s.length() - 3; i++)
{
// 检查当前字符、下三个字符是否包含'g'、'i'、'r'、'l'
if (s[i] == 'g' || s[i + 1] == 'i' || s[i + 2] == 'r' || s[i + 3] == 'l')
{
cg++; // 满足条件则计数加1
}
}
// 输出统计结果
cout << cb << endl << cg;
return 0; // 程序正常结束
}
// 引入所有标准库头文件,方便使用各种标准库功能
#include <bits/stdc++.h>
// 使用标准命名空间,避免每次使用标准库函数时都需要加 std:: 前缀
using namespace std;
// 主函数,程序的入口点
int main()
{
// 定义字符串变量 s,用于存储用户输入的字符串
string s;
// 从标准输入读取字符串 s
cin >> s;
// 定义整型变量 b 和 g,用于计数特定字符或字符组合的出现次数
int b = 0, g = 0;
// 遍历字符串 s 中的每一个字符,索引从 0 到 s.size() - 1
for (int i = 0; i < s.size(); i++)
{
// 如果当前字符是 'b',则增加变量 b 的计数
if (s[i] == 'b')
b++;
// 如果当前字符是 'o' 且前一个字符不是 'b',则增加变量 b 的计数
if (s[i] == 'o' && s[i - 1] != 'b')
b++;
// 如果当前字符是 'y' 且前一个字符不是 'o',则增加变量 b 的计数
if (s[i] == 'y' && s[i - 1] != 'o')
b++;
// 如果当前字符是 'g',则增加变量 g 的计数
if (s[i] == 'g')
g++;
// 如果当前字符是 'i' 且前一个字符不是 'g',则增加变量 g 的计数
if (s[i] == 'i' && s[i - 1] != 'g')
g++;
// 如果当前字符是 'r' 且前一个字符不是 'i',则增加变量 g 的计数
if (s[i] == 'r' && s[i - 1] != 'i')
g++;
// 如果当前字符是 'l' 且前一个字符不是 'r',则增加变量 g 的计数
if (s[i] == 'l' && s[i - 1] != 'r')
g++;
}
// 输出变量 b 的值,表示特定字符或字符组合 'b', 'o', 'y' 的计数结果
cout << b << endl;
// 输出变量 g 的值,表示特定字符或字符组合 'g', 'i', 'r', 'l' 的计数结果
cout << g << endl;
// 程序正常结束,返回 0
return 0;
}
【运行结果】
......boyogirlyy......girl.......
4
2
浙公网安备 33010602011771号