0%

2021 csp-J t4 题解

CCF yyds!

先放上一份debug数据,可以先看看这个试试,如果都没有问题就再看看题解吧

这是输入

1
2
3
4
5
6
7
8
9
10
11
5

Server 10.1.01.2:8080

Server 1.0.100000000000.1:8080

Server 1:0.0.0.0

Server 12..3.9:3

Server ??1.2.3.4:13

输出

1
2
3
4
5
6
7
8
9
ERR

ERR

ERR

ERR

ERR

如果还没看出问题,那就看看这份题解吧。

题目简介

  • 给你 $n$ 个操作,每个操作为创建服务器或连接服务器,在服务器字符串合法下,按照连接情况给出输出:OKFAIL,但如果不合法,输出 ERR

题目解决

这个题大体分为3个步骤:

  1. 输入

  2. 判断地址串是否合法(最重要)。

  3. 判断服务器和客户机,按照情况输出。

重点还是判断地址串是否合法

  1. 地址不得出现除题目规定外的奇奇怪怪字符。

  2. 地址必须要有 5 个数(比如 12..2.6:9 就不行,也就是出现连着的点)。

  3. 地址必须含有 3 个 “.” 与 1 个 “:”,且顺序不能颠倒。

  4. 地址的 5 个数要符合题目规定的数据范围。(可能会超出int大小)

  5. 地址的 5 个数不能含前导 0。

代码实现

1. 服务器地址判断还是有很多坑点的,详见代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
bool check(string str){
int cnt1=0,cnt2=0;
int len=str.size();
for(int i=0;i<len;++i){
if(str[i]=='.')
cnt1++;
if(str[i]==':')
cnt2++;
if(cnt2==1&&cnt1<3)return 0;//1.如果冒号在点之前出现
if((str[i]<'0'||str[i]>'9')&&(str[i]!='.')&&(str[i]!=':'))return 0;//2.出现了奇奇怪怪的字符
}
if(cnt1!=3||cnt2!=1)return 0;
long long int num[5]={-1,-1,-1,-1,-1};//3.不开long long见祖宗
int now=0;
str[len]='.';
for(int i=0;i<len;i++){
if(num[now]==-1&&(str[i]<'0'||str[i]>'9'))
return 0;
else if(num[now]==-1&&(str[i]=='0'&&str[i+1]!='.'&&str[i+1]!=':'))
return 0;//连着都是点
else if(str[i]<'0'||str[i]>'9')
++now;
else if(num[now]==-1)
num[now]=(str[i]&15);
else
num[now]=(num[now]<<1)+(num[now]<<3)+(str[i]&15);//分离数位求和
if(num[now]>65535)return 0;//超过大小
}
if(str[len-1]==':') return 0;
if(num[0]<=255&&num[1]<=255&&num[2]<=255&&num[3]<=255&&num[4]<=65535)//超过大小
return 1;
else return 0;
}

2. 主函数的实现——map。

map 是有序键值对容器,它的元素的键是唯一的。搜索、移除和插入操作拥有对数复杂度。map 通常实现为红黑树。

你可能需要存储一些键值对,例如存储学生姓名对应的分数:Tom 0,Bob 100,Alan 100。但是由于数组下标只能为非负整数,所以无法用姓名作为下标来存储,这个时候最简单的办法就是使用 STL 中的map 了!

用法:可以直接通过下标访问来进行查询或插入操作。例如 mp [Alan] = 100。

选自OI wiki

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
int main(){
string lx,dz;
int n;
cin>>n;
for(int i=1;i<=n;i++){
cin>>lx>>dz;
if(lx[0]=='S'){
if(!check(dz))
cout<<"ERR"<<endl;
else if(M[dz])
cout<<"FAIL"<<endl;
else{
M[dz]=i;//注意这里是行数而不是服务器个数,样例有误导
cout<<"OK"<<endl;
}
}
else{
if(!check(dz))
cout<<"ERR"<<endl;
else if(!M[dz])
cout<<"FAIL"<<endl;
else
cout<<M[dz]<<endl;
}
}
return 0;
}

最后放出ac代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#include<map>
#include<iostream>
using namespace std;
#define N
#define ri register int
bool check(string str){
int cnt1=0,cnt2=0;
int len=str.size();
for(int i=0;i<len;++i){
if(str[i]=='.')
cnt1++;
if(str[i]==':')
cnt2++;
if(cnt2==1&&cnt1<3)return 0;//1.如果冒号在点之前出现
if((str[i]<'0'||str[i]>'9')&&(str[i]!='.')&&(str[i]!=':'))return 0;//2.出现了奇奇怪怪的字符
}
if(cnt1!=3||cnt2!=1)return 0;
long long int num[5]={-1,-1,-1,-1,-1};//3.不开long long见祖宗
int now=0;
str[len]='.';
for(int i=0;i<len;i++){
if(num[now]==-1&&(str[i]<'0'||str[i]>'9'))
return 0;
else if(num[now]==-1&&(str[i]=='0'&&str[i+1]!='.'&&str[i+1]!=':'))
return 0;//连着都是点 e.g:0..0.:70
else if(str[i]<'0'||str[i]>'9')
++now;
else if(num[now]==-1)
num[now]=(str[i]&15);
else
num[now]=(num[now]<<1)+(num[now]<<3)+(str[i]&15);//分离数位求和
if(num[now]>65535)return 0;//超过大小
}
if(str[len-1]==':') return 0;
if(num[0]<=255&&num[1]<=255&&num[2]<=255&&num[3]<=255&&num[4]<=65535)//超过大小
return 1;
else return 0;
}
map<string,int> M;
int main(){
string lx,dz;
int n;
cin>>n;
for(int i=1;i<=n;i++){
cin>>lx>>dz;
if(lx[0]=='S'){
if(!check(dz))
cout<<"ERR"<<endl;
else if(M[dz])
cout<<"FAIL"<<endl;
else{
M[dz]=i;//注意这里是行数而不是服务器个数,样例有误导
cout<<"OK"<<endl;
}
}
else{
if(!check(dz))
cout<<"ERR"<<endl;
else if(!M[dz])
cout<<"FAIL"<<endl;
else
cout<<M[dz]<<endl;
}
}
return 0;//you will ak ioi
}

再放上一份考场代码大家就图一乐,看看就行,a不了),但可以看看思路,思路没问题,应该比ac代码好理解点。