信息学奥赛一本通 2076:【21CSPJ普及组】网络连接(network) | 洛谷 P7911 [CSP-J 2021] 网络连接

【题目链接】

ybt 2076:【21CSPJ普及组】网络连接(network)
洛谷 P7911 [CSP-J 2021] 网络连接

【题目考点】

1. 字符串
2. STL map

【解题思路】

复杂的字符串处理
对各种情况分类讨论,细心处理。

1. 判断字符串是否是合法的ip地址

设函数bool check(string s)来判断字符串s是否是合法的ip地址
题目中对合法的ip地址描述为:

1、必须形如 a.b.c.d:e 的格式,其中 a,b,c,d,e 均为非负整数;
2、0 ≤ a,b,c,d ≤ 255,0 ≤ e ≤ 65535;
3、a,b,c,d,e 均不能含有多余的前导 0。

我们一条一条讨论,每次讨论的情况不要贪多,避免逻辑混乱。

  1. 先确定这个字符串中只有数字字符,‘.‘与’:’。
  2. 确定这个字符串中有3个’.‘,1个’:‘,且’.'在冒号前面。(如果冒号前面没有3个点,则不合法。)
    同时将点和冒号的位置保存在数组中。
  3. 根据点和冒号的位置截取出5段数字字符串。判断数字字符串如果长度为0,那么不合法。
  4. 如果数字字符串第一个字符为0:如果长度为1,那么为数值0,合法。否则即为有前导0,不合法。
  5. 如果数字长度大于6,一定大于65535,数值不符合要求。
  6. 将5个数字字符串转为数值,判断这5个数值的大小是否符合要求。

对于每个输入的ip串,先判断该串是否合法,如不合法,输出ERR。

2. 客户机与服务机连接

解法1:数组保存 顺序查找

本题中服务机与客户机是一对多的关系。
设数组a,a[i]为第i号服务机的ip地址。
如果输入的ip是服务机,那么先查找该服务机是否存在(在数组a上做顺序查找,看能否找到)。

  • 如果存在,输出FAIL
  • 如果不存在,将第i号服务机的地址保存在a[i],输出OK。

如果输入的ip是客户机,那么根据ip串在数组a上查找是否存在该字符串

  • 如果存在,输出该字符串的下标,即服务机编号。
  • 如果不存在,输出FAIL。

由于n最大为1000,即便每次都做顺序查找,整体复杂度为 O ( n 2 ) O(n^2) O(n2),是可以接受的。

解法2:stl map

也可以使用stl map来保存服务机ip与编号之间的关系,设map<string, int> mpmp[s]指ip为s的服务机的编号。
查找该服务机是否存在的方法为:mp.count(),如果返回1,表示存在。如果返回0,表示不存在。
方法与解法1类似。由于map是红黑树,每次查找的复杂度为 O ( l o g n ) O(logn) O(logn),因此该算法整体复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)

【题解代码】

解法1:数组保存 顺序查找
#include <bits/stdc++.h>
using namespace std;
#define N 1005
string a[N];//a[i]表示第i号机的地址(只有部分元素有值,不连续) 
int ai = 0;
//在顺序表中查找字符串,如果存在,返回下标,如果不存在,返回0 
int find(string s)
{
    for(int i = 1; i <= 1000; ++i)
    {
        if(s == a[i])
            return i;
    }
    return 0;
}
//判断ip串是否合法 
bool check(string s)
{
    for(int i = 0; i < s.length(); ++i)//确定s中只有数字、点、冒号 
    {
        if(!(s[i] == '.' || s[i] == ':' || isdigit(s[i])))
            return false;
    }
    int dn = 0, mn = 0;
    vector<int> p;//保存各个符号的下标 
   	for(int i = 0; i < s.length(); ++i) 
	{
	    if(s[i] == '.')
	    {
	        dn++;
	        p.push_back(i);
	    }
        else if(s[i] == ':')
	    {
            mn++;
            if(dn != 3)//如果冒号前面没有3个点,则不合法 
                return false;
            p.push_back(i);
        }
	}
	if(!(dn == 3 && mn == 1))//如果不是有3个点一个冒号,则不合法 
		return false;
	int st = 0;//st:起始位置
	vector<string> ns;//截取出的5个数字字符串
    for(int i = 0; i < p.size(); ++i)//p[0],p[1],p[2]是逗号的下标,p[3]是冒号的下标 
    {
        ns.push_back(s.substr(st, p[i] - st));//st是子串起始位置,p[i]是下一个点或冒号的位置,子串长度为p[i]-st 
        st = p[i] + 1;
    }
	ns.push_back(s.substr(st));//从冒号后一个位置到末尾,是端口号 
    vector<int> vn;//保存数字字符串转成的数字 
    for(int i = 0; i < ns.size(); ++i)
    {
        if(ns[i].length() == 0)//如果哪个数字串长度为0,不合法 
            return false;
        if(ns[i][0] == '0' && ns[i].length() > 1)//如果起始字符为0,且长度大于1,则有前导0。如果长度为1,那么这是数值0,合法。  
            return false;
        if(ns[i].length() > 5)//如果字符串长度大于5,数值一定大于65535,不符合要求。 (如果ns[i]很长,无法使用stoi函数转为数值) 
            return false;
        vn.push_back(stoi(ns[i]));//将ns[i]转为数字 
    }
	for(int i = 0; i < 4; ++i)//判断各个数字是否在规定的范围内 
    {
        if(vn[i] > 255)
            return false;
    }
    if(vn[4] > 65535)
        return false;
	return true;//最后符合要求 
}
int main()
{
	string s_type, s_addr;
	int n, si;
	cin >> n;
	for(int i = 1; i <= n; ++i)
	{
		cin >> s_type >> s_addr;
		if(check(s_addr))
		{
            if(s_type == "Server")
            {
                if(find(s_addr) == 0)
                {
                    a[i] = s_addr;
                    cout << "OK" << endl;
                }
                else
                    cout << "FAIL" << endl;
            }
            else//Client
            {
                si = find(s_addr);
                if(si > 0)
                    cout << si << endl;
                else
                    cout << "FAIL" << endl;
            }
        }
        else
            cout << "ERR" << endl;
	}
    return 0;
}
解法2:stl map
#include <bits/stdc++.h>
using namespace std;
bool check(string s)//判断ip串是否合法 
{
    for(int i = 0; i < s.length(); ++i)//确定s中只有数字、点、冒号 
        if(!(s[i] == '.' || s[i] == ':' || isdigit(s[i])))
            return false;
    int dn = 0, mn = 0, st = 0;
   	vector<string> ns;//截取出的5个数字字符串
	for(int i = 0; i < s.length(); ++i) 
	{
	    if(s[i] == '.')
	    {
	        dn++;
	       	ns.push_back(s.substr(st, i-st));
	       	st = i+1;
	    }
        else if(s[i] == ':')
	    {
            mn++;
            if(dn != 3)//如果冒号前面没有3个点,则不合法 
                return false;
            ns.push_back(s.substr(st, i-st));
        	st = i+1;
		}
	}
	ns.push_back(s.substr(st));//从冒号后一个位置到末尾,是端口号 
	if(!(dn == 3 && mn == 1))//如果不是有3个点一个冒号,则不合法 
		return false;
	vector<int> vn;//保存数字字符串转成的数字 
    for(int i = 0; i < ns.size(); ++i)
    {
        if(ns[i].length() == 0)//如果哪个数字串长度为0,不合法 
            return false;
        if(ns[i][0] == '0' && ns[i].length() > 1)//如果起始字符为0,且长度大于1,则有前导0。如果长度为1,那么这是数值0,合法。  
            return false;
        if(ns[i].length() > 5)//如果字符串长度大于5,数值一定大于65535,不符合要求。 (如果ns[i]很长,无法使用stoi函数转为数值) 
            return false;
        vn.push_back(stoi(ns[i]));//将ns[i]转为数字 
    }
	for(int i = 0; i < 4; ++i)//判断各个数字是否在规定的范围内 
        if(vn[i] > 255)
            return false;
    if(vn[4] > 65535)
        return false;
	return true;//最后符合要求 
}
int main()
{
    map<string, int> mp;//mp[s]指ip为s的服务机的编号。 
	string s_type, s_addr;
	int n, si;
	cin >> n;
	for(int i = 1; i <= n; ++i)
	{
		cin >> s_type >> s_addr;
		if(check(s_addr))
		{
            if(s_type == "Server")
            {
                if(mp.count(s_addr) == 1)//如果存在s_addr 
                    cout << "FAIL" << endl;
                else
                {
                    mp[s_addr] = i;
                    cout << "OK" << endl;
                }
            }
            else//Client
            {
                if(mp.count(s_addr) == 1)//如果存在s_addr 
                    cout << mp[s_addr] << endl;
                else
                    cout << "FAIL" << endl;
            }
        }
        else
            cout << "ERR" << endl;
	}
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值