1. 程式人生 > >每天一道LeetCode-----將字串拆分成有效的ip地址

每天一道LeetCode-----將字串拆分成有效的ip地址

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