跳过并跳转到主要内容
BigO

网络连接

按顺序处理 n 台机器的操作,每台机器分为服务机(Server)或客户机(Client),并附带一个地址串。需要先校验地址串是否符合 a.b.c.d:e 格式(数值不超界且无前导零),若非法输出 ERR;若合法,服务机需在未被占用时建立连接(成功输出 OK,重复输出 FAIL),客户机则尝试连接已建立的服务机(成功输出对应服务机编号,无匹配输出 FAIL)。

CSP-J2分钟阅读

这是一道大模拟与字符串解析题,由于数据规模较小(n≤1000n \le 1000),算法的重点在于精准判断字符串格式是否合法,以及使用高效的数据结构进行服务机地址的匹配。

对于输入的每一行命令,处理分为两步:

    • 先校验地址串是否满足标准的 a.b.c.d:e 格式。
    • 检查数值范围:a,b,c,d∈[0,255]a, b, c, d \in [0, 255],且 e∈[0,65535]e \in [0, 65535]。
    • 检查前导零:除了数字本身为 0 的情况外,不能包含前导零(例如 01 是不合法的)。
    • 若不合法,直接输出 ERR。
  1. 如果操作是 Server:

    • 查询该地址串是否已被成功建立过的服务机占用。
    • 若已占用,输出 FAIL。
    • 若未占用,记录该地址串与当前机器编号 ii 的映射关系,并输出 OK。

    如果操作是 Client:

    • 查询该地址串是否有服务机在监听。
    • 若有,输出该服务机的编号 ii。
    • 若无,输出 FAIL。

可以通过 sscanf 函数结合格式格式化校验,或者手动校验。利用 C++ 的 sscanf 可以非常简洁地解析,但为了防止非法字符绕过,还需要校验重新拼接后的字符串与原字符串是否完全一致。

sscanf 技巧:

利用 sscanf(s.c_str(), "%lld.%lld.%lld.%lld:%lld", &a, &b, &c, &d, &e) 提取 5 个数字。

为了验证格式(是否有多余字符、多余前导零、超出范围),可以将提取出的 a, b, c, d, e 用 sprintf 或 to_string 重新按 a.b.c.d:e 格式拼回字符串 t。

  • 如果 s == t 且数值范围均合法,则原字符串必定完全合法!这种方法能完美规避绝大多数格式陷阱(如前导零、多余的符号等)。

  • 因为需要根据合法地址串快速查找对应的服务机编号,使用 C++ STL 中的 map<string, int> 即可。
  • 插入和查找的时间复杂度为 O(log⁡n)\mathcal{O}(\log n),总时间复杂度为 O(nlog⁡n)\mathcal{O}(n \log n),在 n≤1000n \le 1000 下可以轻松通过(运行时间远小于 1.00s)。

#include <iostream>
#include <string>
#include <map>
#include <cstdio>
using namespace std;
// 检查地址串是否合法
bool check(const string& s) {
long long a, b, c, d, e;
// 尝试按照指定格式解析出 5 个整数
int ret = sscanf(s.c_str(), "%lld.%lld.%lld.%lld:%lld", &a, &b, &c, &d, &e);
// 如果解析出的整数个数不等于 5 个,说明格式错误
if (ret != 5) return false;
// 检查范围是否合法
if (a < 0 || a > 255) return false;
if (b < 0 || b > 255) return false;
if (c < 0 || c > 255) return false;
if (d < 0 || d > 255) return false;
if (e < 0 || e > 65535) return false;
// 重新按格式拼接,验证是否有前导零或多余字符
char buf[100];
sprintf(buf, "%lld.%lld.%lld.%lld:%lld", a, b, c, d, e);
// 原字符串与重新格式化的字符串必须完全一致
return s == string(buf);
}
int main() {
// 优化标准输入输出速度
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
if (!(cin >> n)) return 0;
map<string, int> server_map; // 保存合法服务机的 <地址串, 编号>
for (int i = 1; i <= n; ++i) {
string op, ad;
cin >> op >> ad;
// 1. 检查地址规范
if (!check(ad)) {
cout << "ERR\n";
continue;
}
// 2. 根据操作类型进行处理
if (op == "Server") {
if (server_map.count(ad)) {
cout << "FAIL\n"; // 地址已经被其他服务机占用
} else {
server_map[ad] = i; // 记录服务机编号
cout << "OK\n";
}
} else if (op == "Client") {
if (server_map.count(ad)) {
cout << server_map[ad] << "\n"; // 连接成功,输出服务机编号
} else {
cout << "FAIL\n"; // 找不到可连接的服务机
}
}
}
return 0;
}

  • 时间复杂度: O(n⋅∣S∣log⁡n)\mathcal{O}(n \cdot \vert{}S\vert{} \log n),其中 ∣S∣≤25\vert{}S\vert{} \le 25 为字符串最大长度。每次 check 耗时 O(∣S∣)\mathcal{O}(\vert{}S\vert{}),map 查找/插入耗时 O(∣S∣log⁡n)\mathcal{O}(\vert{}S\vert{} \log n)。总运算量不超过 10510^5 次操作,运行时间在 0.05 秒以内。
  • 空间复杂度: O(n⋅∣S∣)\mathcal{O}(n \cdot \vert{}S\vert{}),主要用于 map 存储建立成功的服务机地址信息。内存开销极小。

© 2026 三七开 · 一方通行,只管向前。

旅途由 Astro 驱动 · 主题 Chirping Astro