题目描述
Malfunction Checkers Ltd (MCL)\texttt{Malfunction Checkers Ltd (MCL)}Malfunction Checkers Ltd (MCL) 公司发现其主服务器的 CCC 编译器已被病毒感染,导致 strcmp() 函数工作异常。该函数在比较字符串时会:
- 忽略第 333 个位置的字符
- 只考虑前 555 个字符来决定返回值
管理员需要编写一个程序来统计电子邮件中的单词出现次数,但必须考虑这个异常的 strcmp() 函数行为。
输入格式
输入包含多封电子邮件,每封以独立的 # 单词结束。输入以 EOF\texttt{EOF}EOF 终止。
每个电子邮件包含多行单词,单词可以包含任何可读字符(除了 #),最大长度为 202020。不同单词的数量限制为 410041004100,但同一单词可能出现多次。
输出格式
每封电子邮件的输出以 Set #n: 开始,以空行结束。
每行显示一个不同的单词(截断到 555 个字符),空格,然后是出现次数。行按单词的升序排列。
关键规则:
- 在排序时,使用每组单词中字典序最小的单词作为排序依据
- 在显示时,使用每组单词中字典序最大的单词作为显示代表
- 忽略第 333 个字符进行比较,但显示时保留实际的第 333 个字符
题目分析
问题本质
这个问题本质上是一个字符串分组统计问题,但比较规则特殊:
- 分组规则:两个单词如果在前 555 个字符中(忽略第 333 个字符)相同,则属于同一组
- 排序规则:按每组中字典序最小的原始单词排序
- 显示规则:显示每组中字典序最大的原始单词(截断到 555 个字符)
样例分析
考虑样例输入:
timk tidk tisk tia tiy tiz
#
分组分析:
- 忽略第 333 个字符后,所有单词的前 555 个字符都变为
ti或ti k tia,tiy,tiz→ 分组键:ti(空格表示忽略的位置)timk,tidk,tisk→ 分组键:ti k
排序分析:
- 第一组最小单词:
tia - 第二组最小单词:
tidk - 由于
tia<tidk,所以第一组排在前面
显示分析:
- 第一组最大单词:
tiz→ 显示:tiz - 第二组最大单词:
tisk→ 显示:tisk
输出:
tiz 3
tisk 3
解题思路
-
分组策略:为每个单词生成一个"排序键",将第 333 个字符替换为固定字符(空格或
#),然后取前 555 个字符 -
数据结构:使用
map<string, vector<string>>来分组存储单词- 键:排序键(忽略第 333 个字符)
- 值:该组的所有原始单词
-
处理逻辑:
- 读取单词,遇到
#时处理当前邮件 - 对于每个单词,计算其排序键并加入对应分组
- 输出时直接遍历
map,利用其自动排序特性 - 显示每组中最后一个加入的单词(通过控制加入顺序保证是最大单词)
- 读取单词,遇到
算法实现
关键技巧
-
利用 Map\texttt{Map}Map 的排序特性:
map会自动按键排序,而我们的排序键正好反映了每组的最小单词特征 -
控制单词加入顺序:通过按输入顺序加入单词,并总是将新单词放在末尾,可以保证
vector的最后一个元素是该组的最大单词 -
简单的键生成:将第 333 个字符替换为空格或
#,有效地"忽略"该位置
复杂度分析
- 时间复杂度:O(NlogM)O(N \log M)O(NlogM),其中 NNN 是单词总数,MMM 是不同分组数
- 空间复杂度:O(N)O(N)O(N),存储所有单词
代码实现
// Count the eWords
// UVa ID: 10352
// Verdict: Accepted
// Submission Date: 2025-11-23
// UVa Run Time: 0.070s
//
// 版权所有(C)2025,邱秋。metaphysis # yeah dot net
#include <bits/stdc++.h>
using namespace std;
string getKey(string s) {
if (s.length() > 2) s[2] = ' ';
return s.substr(0, 5);
}
int main() {
int cases = 1;
string word;
map<string, vector<string>> groups;
while (cin >> word) {
if (word == "#") {
cout << "Set #" << cases++ << ":" << '\n';
for (auto& g : groups)
cout << g.second.back().substr(0, 5) << ' ' << g.second.size() << '\n';
cout << '\n';
groups.clear();
continue;
}
groups[getKey(word)].push_back(word);
}
return 0;
}
总结
本题的关键在于理解异常的 strcmp() 函数行为,并将其转化为有效的分组和排序策略。通过巧妙的键生成方法和利用 STL\texttt{STL}STL 容器的特性,可以以简洁的代码解决这个看似复杂的问题。
这种解法体现了问题转化的思想:将特殊的比较规则转化为标准的数据结构操作,从而简化实现难度。

464

被折叠的 条评论
为什么被折叠?



