每天一道LeetCode-----將字串拆分成有效的ip地址
阿新 • • 發佈:2019-02-06
Restore IP Addresses
給定一個僅包含數字的字串,將其拆分成有效的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中選擇三個位置作為”.”,同時確定分出的四個數字是否合法