先严格校验地址串格式,再用映射表维护成功服务机地址与编号,按顺序处理建立和加入连接。
OJ: luogu
题目 ID: P7911
难度:普及/提高-
标签:字符串模拟
日期: 2026-06-19 10:41
题意
有若干台机器依次发起操作,每次要么是:
Server尝试建立连接;Client尝试加入连接。
每台机器都会给出一个地址串。
你需要先判断地址串是否合法,再根据题目规则输出:
ERROKFAIL- 或连接到的服务机编号
思路
这题分两层处理最清楚。
最直接的教学版写法如下:
cpp
// brute.cpp:按题意直接校验地址并维护已成功建立连接的服务机,作为教学版和对拍基准程序。
#include <bits/stdc++.h>
using namespace std;
int n;
map<string, int> server_id;
// 读取一个必须以 delim 结尾的数字段,并检查范围与前导零。
bool parse_token(const string &s, int &pos, int limit, char delim) {
int n = (int) s.size();
if (pos >= n || s[pos] < '0' || s[pos] > '9') {
return false;
}
int start = pos;
long long value = 0;
while (pos < n && s[pos] >= '0' && s[pos] <= '9') {
value = value * 10 + (s[pos] - '0');
pos++;
}
if (pos - start > 1 && s[start] == '0') {
return false;
}
if (value > limit) {
return false;
}
if (pos >= n || s[pos] != delim) {
return false;
}
pos++;
return true;
}
bool parse_last_token(const string &s, int &pos, int limit) {
int n = (int) s.size();
if (pos >= n || s[pos] < '0' || s[pos] > '9') {
return false;
}
int start = pos;
long long value = 0;
while (pos < n && s[pos] >= '0' && s[pos] <= '9') {
value = value * 10 + (s[pos] - '0');
pos++;
}
if (pos - start > 1 && s[start] == '0') {
return false;
}
if (value > limit) {
return false;
}
return pos == n;
}
bool valid_address(const string &s) {
int pos = 0;
if (!parse_token(s, pos, 255, '.')) return false;
if (!parse_token(s, pos, 255, '.')) return false;
if (!parse_token(s, pos, 255, '.')) return false;
if (!parse_token(s, pos, 255, ':')) return false;
if (!parse_last_token(s, pos, 65535)) return false;
return true;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n;
for (int i = 1; i <= n; i++) {
string type, addr;
cin >> type >> addr;
if (!valid_address(addr)) {
cout << "ERR\n";
continue;
}
if (type == "Server") {
if (server_id.count(addr)) {
cout << "FAIL\n";
}
else {
server_id[addr] = i;
cout << "OK\n";
}
}
else {
if (server_id.count(addr)) {
cout << server_id[addr] << '\n';
}
else {
cout << "FAIL\n";
}
}
}
return 0;
}第一层是地址校验。
地址必须严格形如 a.b.c.d:e,而且:
- 前四段范围在
- 端口范围在
- 不能有多余前导零
第二层是业务逻辑。
我们用一个 map 记录“哪些地址已经被成功建立连接的服务机占用,以及它对应哪台机器编号”。
于是:
Server:地址合法且未被占用,输出OK并记录;否则FAILClient:地址合法且能找到服务机,输出该服务机编号;否则FAIL
代码
cpp
#include <bits/stdc++.h>
using namespace std;
int n;
map<string, int> server_id;
bool parse_token(const string &s, int &pos, int limit, char delim) {
int n = (int) s.size();
if (pos >= n || s[pos] < '0' || s[pos] > '9') {
return false;
}
int start = pos;
long long value = 0;
while (pos < n && s[pos] >= '0' && s[pos] <= '9') {
value = value * 10 + (s[pos] - '0');
pos++;
}
if (pos - start > 1 && s[start] == '0') {
return false;
}
if (value > limit) {
return false;
}
if (pos >= n || s[pos] != delim) {
return false;
}
pos++;
return true;
}
bool parse_last_token(const string &s, int &pos, int limit) {
int n = (int) s.size();
if (pos >= n || s[pos] < '0' || s[pos] > '9') {
return false;
}
int start = pos;
long long value = 0;
while (pos < n && s[pos] >= '0' && s[pos] <= '9') {
value = value * 10 + (s[pos] - '0');
pos++;
}
if (pos - start > 1 && s[start] == '0') {
return false;
}
if (value > limit) {
return false;
}
return pos == n;
}
bool valid_address(const string &s) {
int pos = 0;
if (!parse_token(s, pos, 255, '.')) return false;
if (!parse_token(s, pos, 255, '.')) return false;
if (!parse_token(s, pos, 255, '.')) return false;
if (!parse_token(s, pos, 255, ':')) return false;
if (!parse_last_token(s, pos, 65535)) return false;
return true;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n;
for (int i = 1; i <= n; i++) {
string type, addr;
cin >> type >> addr;
if (!valid_address(addr)) {
cout << "ERR\n";
continue;
}
if (type == "Server") {
if (server_id.count(addr)) {
cout << "FAIL\n";
}
else {
server_id[addr] = i;
cout << "OK\n";
}
}
else {
if (server_id.count(addr)) {
cout << server_id[addr] << '\n';
}
else {
cout << "FAIL\n";
}
}
}
return 0;
}复杂度
- 时间复杂度:
- 空间复杂度:
其中 L 是地址串长度。
总结
这题核心不在映射表,而在地址校验写得够不够严谨。
把地址解析单独写成固定 5 段检查,代码会稳定很多。