leetcode——第93题——复原IP地址

本文分析了一种新思路和传统方法来解决IP地址恢复问题,通过递归和计数小数点控制分支,讲解了如何判断有效IP并进行回溯。两种解法的优缺点及常见错误分析有助于理解IP地址验证的逻辑。

题目:
给定一个只包含数字的字符串,用以表示一个 IP 地址,返回所有可能从 s 获得的 有效 IP 地址 。你可以按任何顺序返回答案。

有效 IP 地址 正好由四个整数(每个整数位于 0 到 255 之间组成,且不能含有前导 0),整数之间用 ‘.’ 分隔。

例如:“0.1.2.201” 和 “192.168.1.1” 是 有效 IP 地址,但是 “0.011.255.245”、“192.168.1.312” 和 “192.168@1.1” 是 无效 IP 地址。

/****************************解法一::一种新思路***********************************/
// 这个方法是根据 起始为 4 ,逐次递减,如果次数减到 0 ,那就说明取够4个段了
// 这个题,把点和数字放一块处理的话,容易踩坑上当
class Solution
{
public:
    vector<string> restoreIpAddresses(string s)
    {   
        // 注意为什么是 4 ,因为IP地址就四个段
        backtracking(s, 0, 4);
        return result;
    }

private:
    vector<string> strs;    // 存放有效地IP
    vector<string> result;  // 存放最终的结果
    // count 用来计每一层的分支个数,每层至多有4个分支
    void backtracking(string& s, int startIndex, int count)
    {
        // 这里写终止逻辑
        if(startIndex == s.size() && count == 0)
        {
            // 当处理完最后一个,且都符合IP地址的定义,在添加小数点
            string str;
            for(auto it : strs)
            {
                str += it;
                str += '.';
            }
            // 这里 substr() 也很妙啊
            result.push_back(str.substr(0, str.size() - 1));
            return;
        }
        if(startIndex == s.size() || count <= 0)
        {
            return;
        }

        int num = 0;
        // (int)s.size() 这里 因为size的类型是 size_t ,直接用 min可能会出错
        // 这里 startIndex+3 的逻辑,与 count 其实是在做一件事儿嘛,
        // 实时证明,他们两个缺一不可
        // XXXXXXX 1 刚刚这里错写成了 startIndex+3 ,整个没有理解到位
        for(int i = startIndex; i < min((int)s.size(), startIndex+3); ++i)
        {
            // 这个方法把判断所切割的字符串是否合法与回溯放一起了
            // XXXXXXX 2 这个里面的也写错了,应该是startIndex,错写成 i 了
            if(i > startIndex && s[startIndex] == '0')   return;
            if(s[i] > '9' || s[i] < '0')    return;
            num = 10 * num + (s[i] - '0');
            if(num > 255)   return;
            // 上面四部用来判断是否合法
            strs.push_back(s.substr(startIndex, i - startIndex + 1));
            // XXXXXXXX 3 这个里面也写错了,把 i 错写成了 startIndex
            backtracking(s, i + 1, count -1);  // count 这里也隐藏了回溯
            strs.pop_back();
        }
    }
};





/****************************解法二::正常套路***********************************/
// 这个方法里判断一层至多有4个是分支,是通过对小数点的计数 count 实现的
// class Solution {
// private:
//     vector<string> result;
//     // string str; // 存储符合条件的集合,不合条件直接就舍弃,没有保存
//     // 函数一:检测所切割出来的字符串是否符合
//     bool isConform(string& s, int start, int end)
//     {
//         // 少一个判断
//         if(start > end)
//         {
//             return false;
//         }
//         // 前置有0应该写在这里
//         if(s[start] == '0' && start != end)
//         {
//             return false;
//         }
//         int num = 0;
//         for(int i = start; i <= end; i++)
//         {
//             // // 1、前置有0
//             // if(i == start && s[i] == 0 && s[i+1] == 0)
//             // {
//             //     return false;
//             // }
//             // 2、特殊字符
//             if(s[i] < '0' || s[i] > '9')
//             {
//                 return false;
//             }
//             num = num * 10 + (s[i] - '0');
//             // 3、不在 0-255的范围
//             // 这个也写错了,应该放在for循环里面,我放在外面了
//             if(num > 255)
//             {
//                 return false;
//             }
//         }
//         return true;
//     }
//     // 函数二:回溯函数
//     void backtracking(string& s, int startIndex, int count)
//     {
//         // IP地址有4个数组成,所以最多有4层递归,那么怎么控制呢?
//         // 通过记录小数点的个数 count
//         // ??????????? 这里应该是几呢?
//         if(count == 3)
//         {
//             // 刚刚的疑问看了答案明白了,因为最后还需要判断第三个小数点之后取得的 str 是否符合,所以这里还有一次判断
//             // 注意::这里最后一个值,肯定是到最后一位的哦!!!刚刚也没想到
//             // 所以这个就是判断第四段是否合法
//             if(isConform(s, startIndex, s.size() - 1))
//             {
//                 result.push_back(s);
//             }

//             return;
//         }

//         // 单层逻辑
//         for(int i = startIndex; i < s.size(); i++)
//         {
//             if(isConform(s, startIndex, i))
//             {
//                 s.insert(s.begin() + i + 1, '.'); // 在i后面加一个小数点
//                 count++;
//                 backtracking(s, i + 2, count);
//                 s.erase(s.begin() + i + 1);   // 删掉小数点,属于回溯的过程
//                 count--;
//             }
//             else
//             {
//                 // 不合法直接结束本层循环,这里可不是 continue 哦,我写错了~~~~
//                 break;
//             }

//         }
//     }
// public:
//     vector<string> restoreIpAddresses(string s) 
//     {
//         if(s.size() < 4 || s.size() > 12)
//         {
//             return result;
//         }
//         int count = 0;
//         backtracking(s, 0, count);
//         return result;
//     }
// };


/************************错误***********错误********错误*************************/
/************************错误***********错误********错误*************************/
/************************错误***********错误********错误*************************/
/******时间紧任务重,放弃这种做法吧~,没有AC成功的代码任由他去吧,回头有空再看看********/
// class Solution {
// private:
//     vector<string> result;
//     string str; // 存储符合条件的集合,不合条件直接就舍弃,没有保存
//     // 函数一:检测所切割出来的字符串是否符合
//     bool isConform(string& s, int start, int end)
//     {
//         // 少一个判断
//         if(start > end)
//         {
//             return false;
//         }
//         // 前置有0,这个是放在for循环外面,且这个逻辑要记忆一下呢,自己没想到
//         if(s[start] == 0 && start != end)
//         {
//             return false;
//         }
//         int num = 0;
//         for(int i = start; i < end; i++)
//         {
//             // 2、特殊字符
//             if(s[i] < '0' || s[i] > '9')
//             {
//                 return false;
//             }
//             num = num * 10 + (s[i] - '0');
//             // 3、不在 0-255的范围,我自己把这个写在for 外面了
//             // 也需要想想清楚,为什么是写在for循环里面,因为在运算的过程中,一旦出现了大于255,继续加肯定还是大于255
//             // 避免了不必要的运算过程
//             if(num > 255)
//             {
//                 return false;
//             }
//         }

//         return true;
//     }
//     // 函数二:回溯函数
//     void backtracking(string& s, int startIndex, int count)
//     {
//         // IP地址有4个数组成,所以最多有4层递归,那么怎么控制呢?
//         // 通过记录小数点的个数 count
//         // ??????????? 这里应该是几呢?
//         if(count == 3)
//         {
//             // 刚刚的疑问看了答案明白了,因为最后还需要判断第三个小数点之后取得的 str 是否符合,所以这里还有一次判断
//             // 注意::这里最后一个值,肯定是到最后一位的哦!!!刚刚也没想到
//             if(isConform(s, startIndex, s.size() - 1))
//             {
//                 str += s.substr(startIndex, s.size() - 1);
//                 result.push_back(str);
//                 str.pop_back;
//             }
//             str.clear();
//             return;
//         }

//         // 单层逻辑
//         for(int i = startIndex; i < s.size(); i++)
//         {
//             if(isConform(s, startIndex, i))
//             {
//                 str += s.substr(startIndex, i - startIndex + 1);
//                 str += '.';
//                 count++;
//                 backtracking(s, i + 1, count);
//                 str.pop_back();
//                 count--;
//             }
//             else
//             {
//                 // 不合法直接结束本层循环,这里可不是 continue 哦,我写错了~~~~
//                 break;
//             }

//         }
//     }
// public:
//     vector<string> restoreIpAddresses(string s) 
//     {
//         if(s.size() < 4 || s.size() > 12)
//         {
//             return result;
//         }
//         int count = 0;
//         backtracking(s, 0, count);
//         return result;
//     }
// };

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值