編程理解——判斷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中選擇三個位置作爲”.”,同時確定分出的四個數字是否合法

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章