复原IP地址

一、前言

问题来源LeetCode

问题链接:https://leetcode-cn.com/explore/interview/card/bytedance/242/string/1044/

 

二、题目

给定一个只包含数字的字符串,复原它并返回所有可能的 IP 地址格式。有效的 IP 地址正好由四个整数(每个整数位于 0 到 255 之间组成),整数之间用 '.' 分隔。

示例:

输入: "25525511135"
输出: ["255.255.11.135", "255.255.111.35"]

三、思路

3.1 题意补充说明

题意中有一点没有说明,输入的数值需要在输出都出现。例如:输入"010010";输出["0.1.0.10"] 不满足要求,少了一个‘0’,也就是还原之后的字符串如果第一个是‘0’则长度只能为1,不能出现类似‘01’,‘01’转成数字之后是1,前面的0被省去了。

3.2 解法

两种解法

方法一:递归解法,每往下走一步都带上上一步的结果

方法二:回溯法

3.3 举例说明

输入:abcdefg,为输入的数组数字范围[1,2],用字母方便区分。

图解方法一:

将上一步的结果集带到下一步,下一步如果有多种结果则和上一步结果进行结合,并将下一步的结果返回给上一步。如下图,结果集的第一步结果可能是a、ab、abc,对于第一步a,它的下一步可能是b、bc、bcd,后面步骤类似处理。直到最后一步。

 

图解方法二:

从第一步到最后一步依次执行,不满足条件则返回继续下一步的执行,到最后一步,将满足条件的结果记录在结果集里。

3.4 对比两种解法

方法一,更符合人正常思考方式,但每往下走一步都需要拷贝一次上一步的结果集,执行效率不好。

方法二,对比方法一不需要进行数据拷贝,按照规则依次往下执行,当不满足条件返回到上一步,继续下一次循环,当走到最后一行并满足条件,将执行结果记录在结果集中。

3.5 总结

方法一提供了一种将思考方式用编码实现,方法二是学习和理解回溯法的好例子。

 

四、编码实现

//==========================================================================
/**
* @file    : 07_RestoreIpAddresses.h
* @blogs   : https://blog.csdn.net/nie2314550441/article/details/106772470
* @author  : niebingyu
* @title   : 复原IP地址
* @purpose : 给定一个只包含数字的字符串,复原它并返回所有可能的 IP 地址格式。有效的 IP 地址正好由四个整数(每个整数位于 0 到 255 之间组成),整数之间用 '.' 分隔。
*
* 示例 1:
* 输入: "25525511135"
* 输出:["255.255.11.135", "255.255.111.35"]
*
* 问题来源:力扣(LeetCode)
* 链接:https://leetcode-cn.com/explore/interview/card/bytedance/242/string/1044/
*/
//==========================================================================
#pragma once
#include <vector>
#include <iostream>
#include <string>
using namespace std;

#define NAMESPACE_RESTOREIPADDRESSES namespace NAME_RESTOREIPADDRESSES {
#define NAMESPACE_RESTOREIPADDRESSESEND }
NAMESPACE_RESTOREIPADDRESSES


#define NODECOUNT 4
class Solution_1 
{
public:
    vector<string> restoreIpAddresses(string s) 
    {
        vector<vector<string>> temp;
        vector<vector<string>> ret = restoreIpAddresses(s, 0, NODECOUNT, temp);
        vector<string> result;
        for (int i = 0; i < ret.size(); ++i)
        {
            string str = "";
            if (ret[i].size() == NODECOUNT)
            {
                for (int j = 0; j < NODECOUNT; ++j)
                {
                    if (j == NODECOUNT - 1)
                        str += ret[i][j];
                    else
                        str += ret[i][j] + ".";
                }

                result.push_back(str);
            }
        }

        return result;
    }

    vector<vector<string>> restoreIpAddresses(string s, int pos, int n, vector<vector<string>> ret)
    {
        if (pos >= s.length() || n <= 0 || (int)s.length() - pos < n || (int)s.length() - pos > n * 3)
            return ret;

        vector<vector<string>> result;
        for (int i = 1; i <= 3 && i + pos <= s.length(); ++i)
        {
            vector<vector<string>> t = ret;
            if ((int)s.length() - pos - i < n - 1 || (int)s.length() - pos - i > (n - 1) * 3)
                continue;

            string str = s.substr(pos, i);
            if (str[0] == '0' && str.length() != 1)
                continue;

            int num = atol(str.c_str());
            if (num < 0 || num > 255)
                break;
            
            if (n == NODECOUNT)
            {
                vector<string> v;
                v.push_back(str);
                t.push_back(v);
            }
            else
            {
                for (int j = 0; j < t.size(); ++j)
                {
                    if (t[j].size() == NODECOUNT - n)
                        t[j].push_back(str);
                }
            }

            vector<vector<string>> tp = restoreIpAddresses(s, pos + i, n - 1, t);
            result.insert(result.end(), tp.begin(), tp.end());
        }

        return result;
    }
};

class Solution_2
{
public:
    vector<string> restoreIpAddresses(string s)
    {
        //找3个.的位置,每个.之前组成的的数值必须要小于255,且以0开头的除非数字是0本身,否则也是非法
        vector<string> res;
        if (s.size() == 0 || s.size() < 4)    return res;
        vector<string> path;//存储从根开始的到叶子节点的满足条件的路径,因为最多3位数字一组,所以同一层横向循环时尝试最多3个位的长度
        dfs(s, 0, path, res);
        return res;
    }

private:
    bool isValid(string ip)
    {
        int val = stoi(ip);
        if (val > 255)    return false;
        if (ip.size() >= 2 && ip[0] == '0')    return false;
        return true;
    }

    void dfs(string& s, int pos, vector<string>& path, vector<string>& res)
    {
        //首先判断剩余的位数,是不是还能满足要求,比如25525511135,若2.5.5.25511135显然不满足,这可以预判
        //4组,每组最多3位数字
        int maxLen = (4 - (int)path.size()) * 3;
        if (s.size() - pos > maxLen)    return;
        if (path.size() == 4 && pos == s.size()) 
        {
            string ans = "";
            for (int i = 0; i < 4; ++i) 
            {
                ans += path[i];
                if (i != 3)    ans += ".";
            }

            res.push_back(ans);
            return;
        }

        //回溯算法的典型模式,循环递归
        for (int i = pos; i < s.size() && i <= pos + 2; ++i) 
        {
            string ip = s.substr(pos, i - pos + 1);
            if (!isValid(ip))    continue;
            path.push_back(ip);
            dfs(s, i + 1, path, res);
            path.pop_back();
        }
    }
};

//
// 测试 用例 START
void test(const char* testName, string s, int expect)
{
    Solution_1 S1;
    Solution_2 S2;
    vector<string> result1 = S1.restoreIpAddresses(s);
    vector<string> result2 = S2.restoreIpAddresses(s);

    // 粗略校验
    if (result1.size() == expect && result2.size() == expect)
        cout << testName << ", solution12 passed." << endl;
    else
        cout << testName << ", solution failed. result1:" << result1.size() << ", result2:" << result2.size() << " ,expect:" << expect << endl;
}

// 测试用例
void Test1()
{
    string s = "255255255255";
    int expect = 1;

    test("Test1()", s, expect);
}

// 测试用例
void Test2()
{
    string s = "25525511135";
    int expect = 2;

    test("Test1()", s, expect);
}

// 测试用例
void Test3()
{
    string s = "010010";
    int expect = 2;

    test("Test1()", s, expect);
}

NAMESPACE_RESTOREIPADDRESSESEND
// 测试 用例 END
//

void RestoreIpAddresses_Test()
{
	NAME_RESTOREIPADDRESSES::Test1();
    NAME_RESTOREIPADDRESSES::Test2();
	NAME_RESTOREIPADDRESSES::Test3();
}

执行结果:

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个可能的解法: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_LEN 12 int isValid(char* s, int start, int end) { if (start > end) { return 0; } if (s[start] == '0' && start != end) { return 0; } int num = 0; for (int i = start; i <= end; i++) { if (s[i] < '0' || s[i] > '9') { return 0; } num = num * 10 + (s[i] - '0'); if (num > 255) { return 0; } } return 1; } void backtrack(char* s, int len, int start, int depth, char* path, char** res, int* returnSize) { if (depth == 4) { if (start == len) { path[strlen(path) - 1] = '\0'; // 去掉最后一个点 res[*returnSize] = (char*) malloc(sizeof(char) * (strlen(path) + 1)); strcpy(res[*returnSize], path); (*returnSize)++; } return; } for (int i = start; i < start + 3 && i < len; i++) { if (isValid(s, start, i)) { char* temp = (char*) malloc(sizeof(char) * (MAX_LEN + 2)); sprintf(temp, "%s%d.", path, atoi(s + start)); backtrack(s, len, i + 1, depth + 1, temp, res, returnSize); free(temp); } } } char** restoreIpAddresses(char* s, int* returnSize) { int len = strlen(s); char** res = (char**) malloc(sizeof(char*) * 5000); *returnSize = 0; if (len < 4 || len > 12) { return res; } char* path = (char*) malloc(sizeof(char) * (MAX_LEN + 2)); backtrack(s, len, 0, 0, path, res, returnSize); free(path); return res; } int main() { int returnSize; char* s = "25525511135"; char** res = restoreIpAddresses(s, &returnSize); for (int i = 0; i < returnSize; i++) { printf("%s\n", res[i]); free(res[i]); } free(res); return 0; } ``` 解题思路和 Python 版本相同,这里就不再赘述。需要注意的是,在 C 语言中,字符串长度是不包括结尾的空字符 `\0` 的,因此每个字符串的申请空间要比 Python 版本多加 1。同时,由于 C 语言不支持字符串加法,所以要使用 `sprintf` 函数将数字转换为字符串后再拼接。最后,记得释放申请的内存。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值