编程理解——判断IP

IP地址的表示一般为A.B.C.D,一共四个数字和三个点号,其中ABCD都是0~255之间的数字。

1、要求判断一个字符串是不是合法的IP地址。

这个字符串的长度应该大于等于7,小于等于15。字符串必须要能分成四部分,而且每部分都是数字字符,分隔符号是点号。以下是自己的代码:

#include <iostream>
#include <string>
using namespace std;
bool judge(string s)
{
   if(s.length()<7||s.length()>15) return false;//满足ip地址的字符个数限制
   int num=0,count=0,a[5]={-1,-1,-1,-1,-1};
    for(int i=0;i<s.length();i++){
        if(s[i]>='0'&&s[i]<='9') num=num*10+s[i]-'0';//如果是数字就保存到num
        else if(s[i]=='.'){//如果遇到点号说明一段数字已经转换完成
            count++;
            a[count]=num;
            num=0;//num要重新置零,计算下一个数
        }
        else return false;
    }
    a[++count]=num;//最后一个数段没有点号分割,别忘记了存储这个数
    for(int i=1;i<5;i++){
        if(a[i]<0||a[i]>255) return false;//如果没有4个数字,那他就是-1,不合法;如果数字大于255也是不合法的
    }
    return true;
}
 
int main()
{ 
    string s1;
    cin>>s1;
	cout<<boolalpha<<judge(s1)<<endl;
	system("pause");
    return 0;
}

2、子网掩码是用来判断任意两台计算机的IP地址是否属于同一子网络的根据。子网掩码与IP地址结构相同,是32位二进制数,其中网络号部分全为“1”和主机号部分全为“0”。利用子网掩码可以判断两台主机是否中同一子网中。若两台主机的IP地址分别与它们的子网掩码相“与”后的结果相同,则说明这两台主机在同一子网中。

* 功能: 判断两台计算机IP地址是同一子网络。 
* 输入参数:    String Mask: 子网掩码,格式:“255.255.255.0”; 
*               String ip1: 计算机1的IP地址,格式:“192.168.0.254”;
*               String ip2: 计算机2的IP地址,格式:“192.168.0.1”;
*               
* 返回值:      0:IP1与IP2属于同一子网络;     1:IP地址或子网掩码格式非法;    2:IP1与IP2不属于同一子网络

代码如下:

#include <iostream>
#include <string>
using namespace std;
 
bool judge(string s,int a[])
{
   if(s.length()<3||s.length()>15) return false;//这里的子网掩码测试用例中有255.0这种样式,所以字符串长度就没有要求必须大于等于7了
    int num=0,count=0;
    for(int i=0;i<s.length();i++){
        if(s[i]>='0'&&s[i]<='9') num=num*10+s[i]-'0';
        else if(s[i]=='.'){
            count++;
            a[count]=num;
            num=0;
        }
        else return false;
    }
    a[++count]=num;
    for(int i=1;i<5;i++){
        if(a[i]<0||a[i]>255) return false;
    }
    return true;
}
bool compa(int a1[],int a2[]){
    for(int i=1;i<5;i++){
        if(a1[i]!=a2[i]) return false;
    }
    return true;
}
 
int main()
{
     
    string s1,s2,s3;
    int a1[5]={0},a2[5]={0},a3[5]={0};
    while(  cin>>s1>>s2>>s3)
    {
        if((judge(s1,a1)&&judge(s2,a2))&&judge(s3,a3)){//判断子网掩码和ip地址的合法性,并把ip地址转换成几个数据段
            for(int i=1;i<5;i++){
                a2[i]=a2[i]&a1[i];//第一个ip与子网掩码相与
                a3[i]=a3[i]&a1[i];//第二个ip与子网掩码相与
            }
            if(compa(a2,a3)) cout<<"0"<<endl;//如果相与之后,四个数字同,就说明他们属于同一个子网,输出0
            else cout<<"2"<<endl;//不属于同一个子网
        }
        else cout<<"1"<<endl;//子网掩码或者ip地址不合法
    }
 
     
    return 0;
}

3、给定一个仅包含数字的字符串,将其拆分成有效的ip地址

分析:实际是在字符串中添加三个点,从而构成一个ip地址,有效的ip地址格式为

  • 最多包含12个数字
  • 每个数字在[0, 255]之间
  • 数字中除了单一0,不能出现类似0开头的数字,比如192.168.1.013中013是不允许的

所以其实就是随便找到三个位置,判断拆分成的四块数字是否满足要求即可

首先,为了易于理解,在安插”.”的过程中不记录组成的ip地址,只是将”.”的位置记录下来,当”.”的个数为3时统一计算

代码如下:

class Solution {
public:
    vector<string> restoreIpAddresses(string s) {
        vector<string> res;
        //不合法
        if(s.size() > 12)   return res;
        //记录"."的位置
        vector<int> dots;
        dfs(s, res, dots, 0);
        return res;
    }
private:
    void dfs(string& s, vector<string>& res, vector<int>& dots, int idx)
    {
        if(dots.size() == 3)
        {
            //判断四个数字是否符合要求,然后添加
            //计算one时的写法是有原因的,可以将dots[-1] 看做 -1
            string one = s.substr(-1 + 1, dots[0] - (-1));
            string two = s.substr(dots[0] + 1, dots[1] - dots[0]);
            string three = s.substr(dots[1] + 1, dots[2] - dots[1]);
            string four = s.substr(dots[2] + 1);
            if(isValid(one) && isValid(two) && isValid(three) && isValid(four))
                res.emplace_back(one + "." + two + "." + three + "." + four);
            return;
        }
        //因为最后一个"."后面必须有数字,所以到s.size() - 1即可
        for(int i = idx; i < static_cast<int>(s.size()) - 1; ++i)
        {
            //表示将"."放在s[i]的后面
            dots.emplace_back(i);
            dfs(s, res, dots, i + 1);
            dots.pop_back();
        }
    }

    bool isValid(string numStr)
    {
        if(numStr.size() > 3 || (numStr.size() > 1 && numStr.find_first_not_of('0') != 0) || (numStr.size() == 3 && numStr > "255"))
            return false;
        else
            return true;
    }
};

这种方法比较慢,主要的原因是会将所有可能都找出来然后判断是否合法,也就是说当确定第一个”.”的位置时,这个位置可能是不合适的,但是仍然需要进行到最后

深度优先和回溯法的思想在于将不合法的情况扼杀在摇篮里,也就是要确定”.”的位置时判断是否满足要求,如果不满足要求,就没必要按照这个”.”的位置进行下去

所以,需要在for循环中动手脚,判断”.”的位置是否合适。方法就是判断当前这个”.”和上一个”.”之间的数字是否符合要求,这里用prevIdx变量记录上一个”.”的位置

由上面计算one,two,three,four的公式可知,两个”.”之间的数字正是[prevIdx+1, i],其中

  • prevIdx记录上一个”.”的位置,初始时为-1,类似公式中的dots[0]
  • i是当前要确定的”.”的位置,指在s[i]后面插入”.”,类似公式中的dots[1]

有了上面的基础,代码可以更改为

class Solution {
public:
    vector<string> restoreIpAddresses(string s) {
        vector<string> res;
        //不合法
        if(s.size() > 12)   return res;
        //记录"."的位置
        vector<int> dots;
        dfs(s, res, dots, -1, 0);
        return res;
    }
private:
    void dfs(string& s, vector<string>& res, vector<int>& dots, int prevIdx, int idx)
    {
        if(dots.size() == 3)
        {
            //判断四个数字是否符合要求,然后添加
            //计算one时的写法是有原因的,可以将dots[-1] 看做 -1
            string one = s.substr(-1 + 1, dots[0] - (-1));
            string two = s.substr(dots[0] + 1, dots[1] - dots[0]);
            string three = s.substr(dots[1] + 1, dots[2] - dots[1]);
            string four = s.substr(dots[2] + 1);
            //one two three在确定"."时已经判断过
            if(isValid(four))
                res.emplace_back(one + "." + two + "." + three + "." + four);
            return;
        }
        //因为最后一个"."后面必须有数字,所以到s.size() - 1即可
        for(int i = idx; i < static_cast<int>(s.size()) - 1; ++i)
        {
            //判断是否满足要求
            if(!isValid(s.substr(prevIdx + 1, i - prevIdx)))
                return;
            //表示将"."放在s[i]的后面
            dots.emplace_back(i);
            dfs(s, res, dots, i, i + 1);
            dots.pop_back();
        }
    }

    bool isValid(string numStr)
    {
        if(numStr.size() > 3 || (numStr.size() > 1 && numStr.find_first_not_of('0') != 0) || (numStr.size() == 3 && numStr > "255"))
            return false;
        else
            return true;
    }
};

再简单一点,可以不需要dots,在遍历的过程中就将最后的ip地址构造好

class Solution {
public:
    vector<string> restoreIpAddresses(string s) {
        vector<string> res;
        //不合法
        if(s.size() > 12)   return res;
        string cur("");
        dfs(s, res, cur, -1, 0, 0);
        return res;
    }
private:
    void dfs(string& s, vector<string>& res, string& cur, int prevIdx, int idx, int count)
    {
        if(count == 3)
        {
            string four = s.substr(idx);
            if(isValid(four))
                res.emplace_back(cur + four);
            return;
        }
        //因为最后一个"."后面必须有数字,所以到s.size() - 1即可
        string tmp = cur;
        for(int i = idx; i < static_cast<int>(s.size()) - 1; ++i)
        {
            //判断是否满足要求
            if(!isValid(s.substr(prevIdx + 1, i - prevIdx)))
                break;

            cur.append(1, s[i]);
            cur.append(1, '.');
            dfs(s, res, cur, i, i + 1, count + 1);
            //回溯的过程需要回到原来的样子,但是这里只弹出了"."的目的是为了继续扩充当前数字
            //不需要回到append(1, s[i])之前的样子,但是return之前需要
            cur.pop_back();
        }
        //当返回时回到原来的样子
        std::swap(cur, tmp);
    }

    bool isValid(string numStr)
    {
        if(numStr.size() > 3 || (numStr.size() > 1 && numStr.find_first_not_of('0') != 0) || (numStr.size() == 3 && numStr > "255"))
            return false;
        else
            return true;
    }
};

本题主要思路是在s中选择三个位置作为”.”,同时确定分出的四个数字是否合法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值