题目分析
本题模拟了基于 SMTP(Simple Mail Transfer Protocol)\texttt{SMTP(Simple Mail Transfer Protocol)}SMTP(Simple Mail Transfer Protocol) 的电子邮件传输过程。题目要求根据给定的 MTA(Message Transfer Agent)\texttt{MTA(Message Transfer Agent)}MTA(Message Transfer Agent) 信息和一系列邮件消息,模拟发送方 MTA 与接收方 MTA\texttt{MTA}MTA 之间的通信过程,并按照指定格式输出通信记录。
关键点:
- 每个 MTA\texttt{MTA}MTA 包含一个名称和一个用户集合。
- 每封邮件有一个发送者和若干个接收者(格式为
用户@MTA名称)。 - 对于每个接收方 MTA\texttt{MTA}MTA,需要建立一次 SMTP\texttt{SMTP}SMTP 会话。
- 在同一个 MTA\texttt{MTA}MTA 内的多个接收者,在一次会话中处理。
- 如果接收方 MTA\texttt{MTA}MTA 中没有某个用户,则返回响应码
550,但这不影响其他有效用户的投递。 - 如果某个 MTA\texttt{MTA}MTA 中没有有效接收者,则不会发送
DATA命令。 - 通信顺序与输入文件中接收方 MTA\texttt{MTA}MTA 出现的顺序一致。
- 输出格式要求:每段会话以
Connection between X and Y开头,每行命令或响应前有 444 个空格,且不应有多余的空格。
解题思路
-
数据结构设计
- 使用结构体
MTA存储 MTA\texttt{MTA}MTA 名称和用户集合(set<string>)。 - 使用
vector<MTA>存储所有 MTA\texttt{MTA}MTA。 - 使用
map<string, int>建立 MTA\texttt{MTA}MTA 名称到索引的映射,便于快速查找。
- 使用结构体
-
输入处理
- 读取 MTA\texttt{MTA}MTA 信息直到遇到单独一行的
*。 - 读取邮件消息直到遇到单独一行的
*(表示结束)。 - 每封邮件的输入格式为:
- 第一行:发送者和所有接收者(空格分隔)。
- 随后一行为
*(消息开始标志)。 - 接下来多行为邮件正文,直到下一个
*(消息结束标志)。
- 读取 MTA\texttt{MTA}MTA 信息直到遇到单独一行的
-
邮件处理流程
- 解析发送者的用户名和 MTA\texttt{MTA}MTA 名称。
- 将所有接收者按目标 MTA\texttt{MTA}MTA 分组,同时去重(同一 MTA\texttt{MTA}MTA 内同一用户只出现一次)。
- 按接收方 MTA\texttt{MTA}MTA 在输入中出现的顺序,逐个处理 SMTP\texttt{SMTP}SMTP 会话。
- 对于每个接收方 MTA\texttt{MTA}MTA:
- 输出连接信息。
- 依次发送
HELO、MAIL FROM命令。 - 对每个接收者发送
RCPT TO命令,根据用户是否存在返回250或550。 - 如果有至少一个有效接收者,则发送
DATA命令,接着发送邮件正文,以单独一行的.结束。 - 发送
QUIT命令结束会话。
-
注意事项
- 无效用户不影响其他有效用户的投递。
- 若一个 MTA\texttt{MTA}MTA中没有有效接收者,则不发送
DATA部分。 - 输出格式必须严格符合要求,包括缩进和空行。
代码实现
// The Letter Carrier's Rounds
// UVa ID: 814
// Verdict: Accepted
// Submission Date: 2025-10-19
// UVa Run Time: 0.030s
//
// 版权所有(C)2025,邱秋。metaphysis # yeah dot net
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <set>
#include <sstream>
using namespace std;
// 存储MTA信息的结构
struct MTA {
string name;
set<string> users;
};
// 解析用户@MTA格式
void parseAddress(const string& address, string& user, string& mta) {
size_t atPos = address.find('@');
user = address.substr(0, atPos);
mta = address.substr(atPos + 1);
}
int main() {
vector<MTA> mtas;
string line;
// 读取MTA信息
while (getline(cin, line) && line != "*") {
if (line[0] == 'M' && line[1] == 'T' && line[2] == 'A') {
istringstream iss(line);
string mta, name;
int userCount;
iss >> mta >> name >> userCount;
MTA newMTA;
newMTA.name = name;
for (int i = 0; i < userCount; i++) {
string user;
iss >> user;
newMTA.users.insert(user);
}
mtas.push_back(newMTA);
}
}
// 创建MTA名称到索引的映射,便于查找
map<string, int> mtaMap;
for (int i = 0; i < mtas.size(); i++) {
mtaMap[mtas[i].name] = i;
}
// 处理消息
while (getline(cin, line) && line != "*") {
// 解析发送者和接收者(都在同一行)
istringstream iss(line);
string sender;
iss >> sender;
string senderUser, senderMTA;
parseAddress(sender, senderUser, senderMTA);
// 读取所有接收者(同一行的剩余部分)
vector<string> allRecipients;
string recipient;
while (iss >> recipient) {
allRecipients.push_back(recipient);
}
// 忽略消息的起始标志*
getline(cin, line);
// 读取消息内容(直到下一个*)
vector<string> messageLines;
while (getline(cin, line) && line != "*") {
messageLines.push_back(line);
}
// 按目标MTA分组接收者
map<string, vector<string>> mtaRecipients;
vector<string> lineMtas;
set<string> mtasCache;
map<string, set<string>> userCache;
for (const auto& recipient : allRecipients) {
string user, mta;
parseAddress(recipient, user, mta);
if (!userCache[mta].count(user)) {
mtaRecipients[mta].push_back(user);
userCache[mta].insert(user);
}
if (!mtasCache.count(mta)) {
mtasCache.insert(mta);
lineMtas.push_back(mta);
}
}
for (const string mta : lineMtas) {
const string& targetMTA = mta;
const vector<string>& users = mtaRecipients[mta];
// 检查目标MTA是否存在
if (mtaMap.find(targetMTA) == mtaMap.end()) {
continue;
}
const MTA& destMTA = mtas[mtaMap[targetMTA]];
// 输出连接信息
cout << "Connection between " << senderMTA << " and " << targetMTA << endl;
// HELO命令
cout << " HELO " << senderMTA << endl;
cout << " 250" << endl;
// MAIL FROM命令
cout << " MAIL FROM:<" << senderUser << "@" << senderMTA << ">" << endl;
cout << " 250" << endl;
// 检查有效接收者
bool hasValidRecipient = false;
for (const string& user : users) {
cout << " RCPT TO:<" << user << "@" << targetMTA << ">" << endl;
if (destMTA.users.find(user) != destMTA.users.end()) {
cout << " 250" << endl;
hasValidRecipient = true;
} else {
cout << " 550" << endl;
}
}
// 如果有有效接收者,发送DATA
if (hasValidRecipient) {
cout << " DATA" << endl;
cout << " 354" << endl;
// 输出消息内容
for (const string& msgLine : messageLines) {
cout << " " << msgLine << endl;
}
// 结束消息
cout << " ." << endl;
cout << " 250" << endl;
}
// QUIT命令
cout << " QUIT" << endl;
cout << " 221" << endl;
}
}
return 0;
}
总结
本题是一道模拟题,重点在于理解 SMTP\texttt{SMTP}SMTP 协议的基本流程,并按照题目要求准确地实现通信逻辑和输出格式。需要注意的细节包括:
- 接收者去重。
- 按 MTA\texttt{MTA}MTA 分组处理。
- 无效用户不影响有效用户。
- 输出格式的缩进和空行。
通过合理的数据结构和清晰的流程控制,可以高效地解决本题。

979

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



