UVa 10352 Count the eWords

题目描述

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 个字符

题目分析

问题本质

这个问题本质上是一个字符串分组统计问题,但比较规则特殊:

  1. 分组规则:两个单词如果在前 555 个字符中(忽略第 333 个字符)相同,则属于同一组
  2. 排序规则:按每组中字典序最小的原始单词排序
  3. 显示规则:显示每组中字典序最大的原始单词(截断到 555 个字符)

样例分析

考虑样例输入:

timk tidk tisk tia tiy tiz
#

分组分析

  • 忽略第 333 个字符后,所有单词的前 555 个字符都变为 titi k
  • tia, tiy, tiz → 分组键:ti (空格表示忽略的位置)
  • timk, tidk, tisk → 分组键:ti k

排序分析

  • 第一组最小单词:tia
  • 第二组最小单词:tidk
  • 由于 tia < tidk,所以第一组排在前面

显示分析

  • 第一组最大单词:tiz → 显示:tiz
  • 第二组最大单词:tisk → 显示:tisk

输出:

tiz 3
tisk 3

解题思路

  1. 分组策略:为每个单词生成一个"排序键",将第 333 个字符替换为固定字符(空格或 #),然后取前 555 个字符

  2. 数据结构:使用 map<string, vector<string>> 来分组存储单词

    • 键:排序键(忽略第 333 个字符)
    • 值:该组的所有原始单词
  3. 处理逻辑

    • 读取单词,遇到 # 时处理当前邮件
    • 对于每个单词,计算其排序键并加入对应分组
    • 输出时直接遍历 map,利用其自动排序特性
    • 显示每组中最后一个加入的单词(通过控制加入顺序保证是最大单词)

算法实现

关键技巧

  1. 利用 Map\texttt{Map}Map 的排序特性map 会自动按键排序,而我们的排序键正好反映了每组的最小单词特征

  2. 控制单词加入顺序:通过按输入顺序加入单词,并总是将新单词放在末尾,可以保证 vector 的最后一个元素是该组的最大单词

  3. 简单的键生成:将第 333 个字符替换为空格或 #,有效地"忽略"该位置

复杂度分析

  • 时间复杂度O(Nlog⁡M)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 容器的特性,可以以简洁的代码解决这个看似复杂的问题。

这种解法体现了问题转化的思想:将特殊的比较规则转化为标准的数据结构操作,从而简化实现难度。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值