1 条题解
-
0
你好!我是你的OI教练。
这道题(CSP-J 2021 T3 网络连接)是一道大模拟题目。这类题目的特点是逻辑分支多、细节要求严,但不需要高深的算法。核心难点在于字符串的合法性判定。
下面我为你提供解题思路和代码提示:
1. 解题思路分解
我们需要处理 条指令,每条指令包含类型(Server/Client)和地址串。
步骤一:判断地址串是否合法(最核心)
一个合法的地址串
a.b.c.d:e必须满足非常严格的条件。手动解析字符串容易漏掉边界情况(比如多余的前导0、数字越界、格式错误等)。这里有一个非常巧妙的技巧:“解析-重构-对比”法。
- 解析:使用
sscanf按照%lld.%lld.%lld.%lld:%lld的格式尝试读取 5 个数字。 - 范围检查:检查读到的数字是否在题目要求的范围内(, )。
- 重构:将读取到的数字用
sprintf重新按照标准格式%lld.%lld.%lld.%lld:%lld输出到一个新的字符串缓冲区。 - 对比:比较“重构后的字符串”与“原始输入字符串”是否完全一致。
- 这个步骤能解决所有棘手问题:
- 如果有前导0(如
01),sscanf会读成1,sprintf写回1,与原串01不相等 判错。 - 如果有非法字符(如
1.1.1.1:80abc),sscanf读完数字后停止,sprintf只有数字部分,与原串不相等 判错。 - 如果格式不对(如
1.1.1.1.80),sscanf无法读满 5 个数 判错。
步骤二:逻辑处理
使用一个 哈希表(Map) 来存储已经成功建立连接的服务机。
map<string, int> mp;- Key: 地址串(string)
- Value: 服务机的编号(int)
对于第 条指令:
- 检查合法性:如果不合法,输出
ERR。 - 如果是 Server:
- 检查 Map 中是否已存在该地址。
- 存在 输出
FAIL。 - 不存在 输出
OK,并将{地址, i}存入 Map。
- 如果是 Client:
- 检查 Map 中是否已存在该地址。
- 存在 输出 Map 中对应的编号。
- 不存在 输出
FAIL。
2. 标准 AC 代码
#include <iostream> #include <string> #include <map> #include <cstdio> // 用于 sscanf 和 sprintf using namespace std; // 核心函数:检查字符串 s 是否符合规范 bool check(string s) { long long a, b, c, d, e; // 1. 尝试按照特定格式提取 5 个数字 // s.c_str() 将 C++ string 转为 C 风格字符串 int count = sscanf(s.c_str(), "%lld.%lld.%lld.%lld:%lld", &a, &b, &c, &d, &e); // 必须成功读取到 5 个数字 if (count != 5) return false; // 2. 检查数字范围 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; // 3. "解析-重构-对比" 验证法 // 将提取出的数字重新按标准格式印回字符串 char buf[100]; sprintf(buf, "%lld.%lld.%lld.%lld:%lld", a, b, c, d, e); // 如果原字符串是 "192.168.0.1:080" (含前导0) // sscanf 读出 e=80 // sprintf 写回 buf="192.168.0.1:80" // 此时 buf != s,说明不规范 string res = buf; return res == s; } int main() { int n; cin >> n; // 映射表:存储已成功的 Server 地址 -> 编号 map<string, int> servers; for (int i = 1; i <= n; i++) { string op, ad; cin >> op >> ad; // 1. 先判断合法性 if (!check(ad)) { cout << "ERR" << endl; continue; } // 2. 根据类型处理 if (op == "Server") { if (servers.count(ad)) { // 地址已存在 cout << "FAIL" << endl; } else { // 成功建立 servers[ad] = i; cout << "OK" << endl; } } else { // Client if (servers.count(ad)) { // 找到对应的服务机,输出编号 cout << servers[ad] << endl; } else { // 未找到 cout << "FAIL" << endl; } } } return 0; }3. 注意事项
- 数据类型:解析 IP 地址的数字时,建议使用
long long。虽然合法的数字不超过 65535,但输入可能会给出一个超级大的数字(如1.1.1.1:99999999999),使用int可能会溢出,而sscanf配合long long能正确读取并进行后续的范围判断。 sscanf返回值:它返回成功匹配并赋值的参数个数。- Map 的复杂度:
map的查询和插入复杂度是 ,其中 是字符串长度。本题 ,非常快,完全不会超时。
希望这个思路能帮你顺利解决这道大模拟题!加油!
- 解析:使用
- 1
信息
- ID
- 12044
- 时间
- 1000ms
- 内存
- 32MiB
- 难度
- 5
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者