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