计算从原始IP字符串中获得的所有有效IP地址

huangapple go评论115阅读模式
英文:

Computing All Valid IP Addresses From Raw IP String

问题

public List<String> restoreIpAddresses(String s) {
    List<String> res = new ArrayList<>();
    if (s.length() == 0) {
        return res;
    }
    int[] path = new int[4];
    snapshotIP(res, s, 0, path, 0);
    return res;
}

public void snapshotIP(List<String> res, String s, int index, int[] path, int segment) {
    if (segment == 4 && index == s.length()) {
        res.add(path[0] + "." + path[1] + "." + path[2] + "." + path[3]);
        return;
    } else if (segment == 4 || index == s.length()) {
        return;
    }
    for (int len = 1; len <= 3 && index + len <= s.length(); len++) {
        String snap = s.substring(index, index + len);
        int val = Integer.parseInt(snap);
        if (val > 225 || len >= 2 && s.charAt(index) == '0') {
            break;
        }
        path[segment] = val;
        snapshotIP(res, s, index + len, path, segment + 1);
        path[segment] = -1; //undo the choice
    }
}
英文:

I'm now solving leetcode problem 93. Restore IP Addresses.

Here's the url link: https://leetcode.com/problems/restore-ip-addresses/

The description looks like this:
Given a string s containing only digits. Return all possible valid IP addresses that can be obtained from s. You can return them in any order.

A valid IP address consists of exactly four integers, each integer is between 0 and 255, separated by single points and cannot have leading zeros. For example, "0.1.2.201" and "192.168.1.1" are valid IP addresses and "0.011.255.245", "192.168.1.312" and "192.168@1.1" are invalid IP addresses.

However, as I was trying to solve my problem via backtracking, I couldn't figure out the reason that I'm always returning an empty ArrayList. I double checked my base case and my recursion and still couldn't find the bug. Any help would be greatly appreciated, thank you!

public List&lt;String&gt; restoreIpAddresses(String s) {
        List&lt;String&gt; res = new ArrayList&lt;&gt;();
        if(s.length() == 0){
            return res;
        }
        int[] path = new int[4];
        snapshotIP(res,s,0,path,0);
        return res;
    }
    
    public void snapshotIP(List&lt;String&gt; res, String s, int index, int[] path, int segment){
        if(segment == 4 &amp;&amp; index == s.length()){
            res.add(path[0]+&quot;.&quot;+path[1]+&quot;.&quot;+path[2]+&quot;.&quot;+path[3]);
            return;
        }
        else if(segment == 4 || index == s.length()){
            return;
        }
        for(int len = 1; len &lt;= 3 &amp;&amp; index + len &lt;= s.length(); len++){
            String snap = s.substring(index,index+len);
            int val = Integer.parseInt(snap);
            if(val &gt; 225 || len &gt;= 2 &amp;&amp; s.charAt(index) == &#39;0&#39;){
                break;
            }
            path[segment] = val;
            snapshotIP(res,s,index+len,path,segment+1);
            path[segment] = -1; //undo the choice

        }
    }

答案1

得分: 4

你已经编写了一段相当高级的代码。对于所有IP地址段低于225的情况,它都能正常工作,但第一个测试案例中包含了255。

修复方法很简单,只需将"val > 225"替换为"val > 255"。

代码应该如下所示:

<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-java -->

if (val > 255 || len >= 2 && s.charAt(index) == '0')

<!-- end snippet -->

附言:
我会以不同的方式处理这个问题,我会在所有可能的位置添加点,并验证每个组合的有效性。

英文:

You have written a pretty advanced code. It's working for all the cases where IP address segment is lower than 225, but the first test case has 255s in there.

The fix is trivial, just replace "val > 225" to "val > 255".

It should be like this:
<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-java -->

if(val &gt; 255 || len &gt;= 2 &amp;&amp; s.charAt(index) == &#39;0&#39;)

<!-- end snippet -->

P.S.
I would do this differently, I would add dots into every possible place and validate every received combination.

答案2

得分: 0

你的代码看起来还不错,一点问题都没有,不过我不确定你的错误在哪里。

以下是一个备选方案,虽然不太好看,但应该可以通过测试:

public final class Solution {
    public static final List<String> restoreIpAddresses(
        final String ip
    ) {
        List<String> res = new ArrayList<>();
        int length = ip.length();

        for (int i = 1; i < 4 && i < length - 2; i++)
            for (int j = i + 1; j < i + 4 && j < length - 1; j++)
                for (int k = j + 1; k < j + 4 && k < length; k++) {
                    final String part1 = ip.substring(0, i);
                    final String part2 = ip.substring(i, j);
                    final String part3 = ip.substring(j, k);
                    final String part4 = ip.substring(k, length);

                    if (isValid(part1) && isValid(part2) && isValid(part3) && isValid(part4)) {
                        res.add(part1 + "." + part2 + "." + part3 + "." + part4);
                    }
                }

        return res;
    }

    private static final boolean isValid(
        final String s
    ) {
        if (s.length() > 3 || s.length() == 0 || (s.charAt(0) == '0' && s.length() > 1) || Integer.parseInt(s) > 255) {
            return false;
        }

        return true;
    }
}

你的代码中有点可疑的地方是回溯辅助函数是 void 类型的,也许你需要定义一个变量使其正常工作,但还不确定。

类似地,在 C++ 中,如果你有兴趣的话:

// 以下代码块可能会稍微提高执行速度;
// 可以删除;
static const auto __optimize__ = []() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(NULL);
    std::cout.tie(NULL);
    return 0;
}();

// 大多数头文件已经包含;
// 可以删除;
#include <cstdint>
#include <vector>
#include <string>

#define LIMIT 256

using ValueType = std::uint_fast16_t;

static const struct Solution {
    static const std::vector<std::string> restoreIpAddresses(
        const std::string s
    ) {
        const ValueType len = std::size(s);
        std::vector<std::string> ips;
        std::string ip;
        ValueType a, b, c, d;
        ValueType A, B, C, D;

        for (a = 1; a < 4; ++a) {
            for (b = 1; b < 4; ++b) {
                for (c = 1; c < 4; ++c) {
                    for (d = 1; d < 4; ++d) {
                        if (a + b + c + d == len) {
                            A = std::stoi(s.substr(0, a));
                            B = std::stoi(s.substr(a, b));
                            C = std::stoi(s.substr(a + b, c));
                            D = std::stoi(s.substr(a + b + c, d));

                            if (A < LIMIT && B < LIMIT && C < LIMIT && D < LIMIT) {
                                ip = std::to_string(A) + "." +
                                     std::to_string(B) + "." +
                                     std::to_string(C) + "." +
                                     std::to_string(D);

                                if (std::size(ip) == len + 3) {
                                    ips.emplace_back(ip);
                                }
                            }
                        }
                    }
                }
            }
        }

        return ips;
    }
};

这里是类似于你的 LeetCode 回溯深度优先搜索算法,可能会帮助你找到问题所在:

class Solution {
  int n;
  String s;
  LinkedList<String> segments = new LinkedList<String>();
  ArrayList<String> output = new ArrayList<String>();

  public boolean valid(String segment) {
    int m = segment.length();
    if (m > 3)
      return false;
    return (segment.charAt(0) != '0') ? (Integer.valueOf(segment) <= 255) : (m == 1);
  }

  public void update_output(int curr_pos) {
    String segment = s.substring(curr_pos + 1, n);
    if (valid(segment)) {
      segments.add(segment);
      output.add(String.join(".", segments));
      segments.removeLast();
    }
  }

  public void backtrack(int prev_pos, int dots) {
    int max_pos = Math.min(n - 1, prev_pos + 4);
    for (int curr_pos = prev_pos + 1; curr_pos < max_pos; curr_pos++) {
      String segment = s.substring(prev_pos + 1, curr_pos + 1);
      if (valid(segment)) {
        segments.add(segment);
        if (dots - 1 == 0)
          update_output(curr_pos);
        else
          backtrack(curr_pos, dots - 1);
        segments.removeLast();
      }
    }
  }

  public List<String> restoreIpAddresses(String s) {
    n = s.length();
    this.s = s;
    backtrack(-1, 3);
    return output;
  }
}

参考资料:

  • 如需更多详细信息,请参阅 讨论板,在那里你可以找到许多解释详细的已接受解决方案,涵盖了各种 语言,包括低复杂度的算法和渐进 运行时间/内存 分析1, 2
英文:

Your code looks pretty good, not bad at all, not sure where your bug is though.

Here is an alternative solution, not so pretty though, it would pass just fine:

public final class Solution {
public static final List&lt;String&gt; restoreIpAddresses(
final String ip
) {
List&lt;String&gt; res = new ArrayList&lt;&gt;();
int length = ip.length();
for (int i = 1; i &lt; 4 &amp;&amp; i &lt; length - 2; i++)
for (int j = i + 1; j &lt; i + 4 &amp;&amp; j &lt; length - 1; j++)
for (int k = j + 1; k &lt; j + 4 &amp;&amp; k &lt; length; k++) {
final String part1 = ip.substring(0, i);
final String part2 = ip.substring(i, j);
final String part3 = ip.substring(j, k);
final String part4 = ip.substring(k, length);
if (isValid(part1) &amp;&amp; isValid(part2) &amp;&amp; isValid(part3) &amp;&amp; isValid(part4)) {
res.add(part1 + &quot;.&quot; + part2 + &quot;.&quot; + part3 + &quot;.&quot; + part4);
}
}
return res;
}
private static final boolean isValid(
final String s
) {
if (s.length() &gt; 3 || s.length() == 0 || (s.charAt(0) == &#39;0&#39; &amp;&amp; s.length() &gt; 1) || Integer.parseInt(s) &gt; 255) {
return false;
}
return true;
}
}

Something a bit suspicious in your code is that the backtracking helper function is void, maybe you've to define a variable to make it work, still unsure.


Similarly in C++, if you'd be interested:

// The following block might slightly improve the execution time;
// Can be removed;
static const auto __optimize__ = []() {
std::ios::sync_with_stdio(false);
std::cin.tie(NULL);
std::cout.tie(NULL);
return 0;
}();
// Most of headers are already included;
// Can be removed;
#include &lt;cstdint&gt;
#include &lt;vector&gt;
#include &lt;string&gt;
#define LIMIT 256
using ValueType = std::uint_fast16_t;
static const struct Solution {
static const std::vector&lt;std::string&gt; restoreIpAddresses(
const std::string s
) {
const ValueType len = std::size(s);
std::vector&lt;std::string&gt; ips;
std::string ip;
ValueType a, b, c, d;
ValueType A, B, C, D;
for (a = 1; a &lt; 4; ++a) {
for (b = 1; b &lt; 4; ++b) {
for (c = 1; c &lt; 4; ++c) {
for (d = 1; d &lt; 4; ++d) {
if (a + b + c + d == len) {
A = std::stoi(s.substr(0, a));
B = std::stoi(s.substr(a, b));
C = std::stoi(s.substr(a + b, c));
D = std::stoi(s.substr(a + b + c, d));
if (A &lt; LIMIT &amp;&amp; B &lt; LIMIT &amp;&amp; C &lt; LIMIT &amp;&amp; D &lt; LIMIT) {
ip = std::to_string(A) + &quot;.&quot; +
std::to_string(B) + &quot;.&quot; +
std::to_string(C) + &quot;.&quot; +
std::to_string(D);
if (std::size(ip) == len + 3) {
ips.emplace_back(ip);
}
}
}
}
}
}
}
return ips;
}
};

Here is LeetCode's backtracking depth first search algorithm that is similar to yours, that might help you figure it out.

class Solution {
int n;
String s;
LinkedList&lt;String&gt; segments = new LinkedList&lt;String&gt;();
ArrayList&lt;String&gt; output = new ArrayList&lt;String&gt;();
public boolean valid(String segment) {
/*
Check if the current segment is valid :
1. less or equal to 255      
2. the first character could be &#39;0&#39; 
only if the segment is equal to &#39;0&#39;
*/
int m = segment.length();
if (m &gt; 3)
return false;
return (segment.charAt(0) != &#39;0&#39;) ? (Integer.valueOf(segment) &lt;= 255) : (m == 1);
}
public void update_output(int curr_pos) {
/*
Append the current list of segments 
to the list of solutions
*/
String segment = s.substring(curr_pos + 1, n);
if (valid(segment)) {
segments.add(segment);
output.add(String.join(&quot;.&quot;, segments));
segments.removeLast();
}
}
public void backtrack(int prev_pos, int dots) {
/*
prev_pos : the position of the previously placed dot
dots : number of dots to place
*/
// The current dot curr_pos could be placed 
// in a range from prev_pos + 1 to prev_pos + 4.
// The dot couldn&#39;t be placed 
// after the last character in the string.
int max_pos = Math.min(n - 1, prev_pos + 4);
for (int curr_pos = prev_pos + 1; curr_pos &lt; max_pos; curr_pos++) {
String segment = s.substring(prev_pos + 1, curr_pos + 1);
if (valid(segment)) {
segments.add(segment);  // place dot
if (dots - 1 == 0)      // if all 3 dots are placed
update_output(curr_pos);  // add the solution to output
else
backtrack(curr_pos, dots - 1);  // continue to place dots
segments.removeLast();  // remove the last placed dot 
}
}
}
public List&lt;String&gt; restoreIpAddresses(String s) {
n = s.length();
this.s = s;
backtrack(-1, 3);
return output;
}
}

References

  • For additional details, please see the Discussion Board where you can find plenty of well-explained accepted solutions with a variety of languages including low-complexity algorithms and asymptotic runtime/memory analysis<sup>1, 2</sup>.

答案3

得分: 0

internal static IEnumerable<string> FetchPossibleIPs(string input, int currentSection = 1)
{
    List<string> possibleIPs = new List<string>();
    if (input.Length > 0)
    {
        // 如果当前部分是第4部分,则无需继续拆分,直接验证。
        if (currentSection == 4)
        {
            if (int.Parse(input) <= 255 && input[0].ToString() != "0")
            {
                possibleIPs.Add(input);
            }
        }
        // 如果当前部分小于4,则通过长度为1、2或3的子字符串进行拆分,
        // 以找出可能的组合。
        else
        {
            for (int i = 1; i <= 3; ++i)
            {
                var section = input.Substring(0, i);
                if (int.Parse(section) <= 255 && section[0].ToString() != "0")
                {
                    var otherSections = FetchPossibleIPs(input.Substring(i), currentSection + 1);
                    foreach (var item in otherSections)
                    {
                        possibleIPs.Add($"{section}.{item}");
                    }
                }
            }
        }
    }
    return possibleIPs;
}

使用递归和回溯解决该问题的C#示例解决方案。

英文:
internal static IEnumerable&lt;string&gt; FetchPossibleIPs(string input, int currentSection = 1)
{
List&lt;string&gt; possibleIPs = new List&lt;string&gt;();
if (input.Length &gt; 0)
{
// If section is 4 then no need 
// to break further and simply verify.
if (currentSection == 4)
{
if (int.Parse(input) &lt;= 255 &amp;&amp; input[0].ToString() != &quot;0&quot;)
{
possibleIPs.Add(input);
}
}
// Else if section is &lt; 4 then break the string 
// with substring of length of 1,2 or 3 to 
// figure out possible combinations.
else
{
for (int i = 1; i &lt;= 3; ++i)
{
var section = input.Substring(0, i);
if (int.Parse(section) &lt;= 255 &amp;&amp; section[0].ToString() != &quot;0&quot;)
{
var otherSections = FetchPossibleIPs(input.Substring(i), currentSection + 1);
foreach (var item in otherSections)
{
possibleIPs.Add($&quot;{section}.{item}&quot;);
}
}
}
}
}
return possibleIPs;
}

A sample solution in C# using recursion to solve it.
It uses recursion and backtracking to solve the problem.

huangapple
  • 本文由 发表于 2020年8月28日 10:23:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/63626593.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定