# Validate IP Address
**Difficulty:** MEDIUM
[External](https://leetcode.com/problems/validate-ip-address)
Canonical: https://scaleengineer.com/dsa/problems/validate-ip-address
**Data structures:** String
**Companies:** [Cisco](https://scaleengineer.com/companies/cisco), [Deutsche Bank](https://scaleengineer.com/companies/deutsche-bank), [Nvidia](https://scaleengineer.com/companies/nvidia), [ServiceNow](https://scaleengineer.com/companies/servicenow), [Turing](https://scaleengineer.com/companies/turing), [X](https://scaleengineer.com/companies/x), [Sprinklr](https://scaleengineer.com/companies/sprinklr), [Flexport](https://scaleengineer.com/companies/flexport)
---
## Problem
Given a string `queryIP`, return `"IPv4"` if IP is a valid IPv4 address, `"IPv6"` if IP is a valid IPv6 address or `"Neither"` if IP is not a correct IP of any type.

**A valid IPv4** address is an IP in the form `"x1.x2.x3.x4"` where `0 <= xi <= 255` and `xi` **cannot contain** leading zeros. For example, `"192.168.1.1"` and `"192.168.1.0"` are valid IPv4 addresses while `"192.168.01.1"`, `"192.168.1.00"`, and `"192.168@1.1"` are invalid IPv4 addresses.

**A valid IPv6** address is an IP in the form `"x1:x2:x3:x4:x5:x6:x7:x8"` where:

* `1 <= xi.length <= 4`
* `xi` is a **hexadecimal string** which may contain digits, lowercase English letter (`'a'` to `'f'`) and upper-case English letters (`'A'` to `'F'`).
* Leading zeros are allowed in `xi`.

For example, "`2001:0db8:85a3:0000:0000:8a2e:0370:7334"` and "`2001:db8:85a3:0:0:8A2E:0370:7334"` are valid IPv6 addresses, while "`2001:0db8:85a3::8A2E:037j:7334"` and "`02001:0db8:85a3:0000:0000:8a2e:0370:7334"` are invalid IPv6 addresses.

**Example 1:**

**Input:** queryIP = "172.16.254.1"
**Output:** "IPv4"
**Explanation:** This is a valid IPv4 address, return "IPv4".

**Example 2:**

**Input:** queryIP = "2001:0db8:85a3:0:0:8A2E:0370:7334"
**Output:** "IPv6"
**Explanation:** This is a valid IPv6 address, return "IPv6".

**Example 3:**

**Input:** queryIP = "256.256.256.256"
**Output:** "Neither"
**Explanation:** This is neither a IPv4 address nor a IPv6 address.

**Constraints:**

* `queryIP` consists only of English letters, digits and the characters `'.'` and `':'`.

# Approaches
## Regular Expression Matching
This approach leverages the power of regular expressions (regex) to validate the IP address formats. We define two regex patterns, one for IPv4 and one for IPv6, and check if the input string matches either of them. This method is concise but can be less intuitive for those unfamiliar with regex syntax.
**Time:** O(1). The length of a valid IP address string is bounded by a constant (e.g., max 39 chars for IPv6). Regex matching on a fixed-size string takes constant time. · **Space:** O(1). The space used to store the compiled patterns is constant and does not depend on the input string size.
**Pros:** The code is very short and declarative, expressing the validation logic compactly.; It relies on a standard, well-tested library feature for pattern matching, which can reduce bugs.
**Cons:** Regular expressions can be complex to write, read, and debug, especially for nuanced rules like the leading zero constraint in IPv4.; The performance can be slightly slower than manual parsing due to the overhead of the general-purpose regex engine, although this is often negligible for fixed-size inputs.
### Explanation
The core idea is to construct precise regular expressions that capture all the rules for valid IPv4 and IPv6 addresses.

*   **IPv4 Validation:**
    *   An IPv4 address consists of four parts separated by dots. Each part is a number from 0 to 255. A crucial rule is that there are no leading zeros, unless the number is 0 itself.
    *   The regex for one part is `(0|[1-9][0-9]?|1[0-9]{2}|2[0-4][0-9]|25[0-5])`.
    *   The full IPv4 regex is `^((...)\.){3}(...)$`, where `(...)` is the pattern for one part.

*   **IPv6 Validation:**
    *   An IPv6 address consists of eight parts separated by colons. Each part is a hexadecimal string of length 1 to 4.
    *   The regex for one part is `[0-9a-fA-F]{1,4}`.
    *   The full IPv6 regex is `^((...):){7}(...)$`, where `(...)` is the pattern for one part.

The implementation first checks the count of delimiters to quickly filter out invalid formats before applying the more expensive regex match.

```java
import java.util.regex.Pattern;

class Solution {
    public String validIPAddress(String queryIP) {
        String ipv4Chunk = "(0|[1-9][0-9]?|1[0-9]{2}|2[0-4][0-9]|25[0-5])";
        String ipv4Pattern = "^(" + ipv4Chunk + "\\.){3}" + ipv4Chunk + "$";

        String ipv6Chunk = "[0-9a-fA-F]{1,4}";
        String ipv6Pattern = "^(" + ipv6Chunk + ":){7}" + ipv6Chunk + "$";

        if (queryIP.chars().filter(ch -> ch == '.').count() == 3) {
            if (Pattern.matches(ipv4Pattern, queryIP)) {
                return "IPv4";
            }
        }
        
        if (queryIP.chars().filter(ch -> ch == ':').count() == 7) {
            if (Pattern.matches(ipv6Pattern, queryIP)) {
                return "IPv6";
            }
        }
        
        return "Neither";
    }
}
```
### Algorithm
- Define a regular expression `ipv4Pattern` that matches the specific format of a valid IPv4 address. This pattern must account for:
  - Four numeric parts separated by `.`.
  - Each part being a value between 0 and 255.
  - The rule against leading zeros (e.g., `01` is invalid, but `0` is valid).
- Define a second regular expression `ipv6Pattern` for valid IPv6 addresses, which must account for:
  - Eight parts separated by `:`.
  - Each part being a hexadecimal string of 1 to 4 characters.
- First, check if the input string `queryIP` has 3 dots. If so, attempt to match it against `ipv4Pattern`. If it matches, return `"IPv4"`.
- Next, check if the input string has 7 colons. If so, attempt to match it against `ipv6Pattern`. If it matches, return `"IPv6"`.
- If neither of the conditions is met, return `"Neither"`.

## String Processing and Manual Validation
This approach involves directly parsing the input string. We first determine if the string is a potential IPv4 or IPv6 address based on the delimiter it contains ('.' or ':'). Then, we split the string into parts and manually validate each part against the specific rules for that IP version. This method is more verbose but offers clear, step-by-step logic and is generally more performant.
**Time:** O(1). The input string's length is bounded by a constant. The `split` operation and subsequent loops run in time proportional to this small, fixed length, resulting in constant time complexity. · **Space:** O(1). The space required for the `parts` array is constant, as there are at most 8 parts. The size does not scale with the input length.
**Pros:** The logic is explicit and easy to follow, making the code highly readable and maintainable.; Generally more performant than regex because it avoids the overhead of a general-purpose pattern matching engine.; Debugging is straightforward as you can step through each validation check.
**Cons:** The code is more verbose compared to a regex solution.; Requires careful implementation to handle all validation rules and edge cases correctly, such as trailing delimiters or empty parts.
### Explanation
The logic is divided into two main validation functions, one for IPv4 and one for IPv6, which are called after a preliminary check on the delimiter count.

*   **IPv4 Validation (`validateIPv4`):**
    1.  Split the input string by the dot `.` character. Using `ip.split("\\.", -1)` is crucial to handle cases with trailing dots correctly.
    2.  Check if the resulting array has exactly four parts.
    3.  Iterate through each part, applying checks for length (1-3), no invalid leading zeros, all characters being digits, and the numeric value being in the [0, 255] range.

*   **IPv6 Validation (`validateIPv6`):**
    1.  Split the input string by the colon `:` character using `ip.split(":", -1)`.
    2.  Check if the resulting array has exactly eight parts.
    3.  Iterate through each part, verifying its length (1-4) and ensuring all its characters are valid hexadecimal digits.

If all checks for a given IP type pass, the corresponding string (`"IPv4"` or `"IPv6"`) is returned. Otherwise, the function returns `"Neither"`.

```java
class Solution {
    public String validIPAddress(String queryIP) {
        if (queryIP == null || queryIP.length() == 0) {
            return "Neither";
        }
        if (queryIP.chars().filter(ch -> ch == '.').count() == 3) {
            return validateIPv4(queryIP);
        } else if (queryIP.chars().filter(ch -> ch == ':').count() == 7) {
            return validateIPv6(queryIP);
        }
        return "Neither";
    }

    private String validateIPv4(String ip) {
        String[] parts = ip.split("\\.", -1);
        if (parts.length != 4) {
            return "Neither";
        }

        for (String part : parts) {
            if (part.length() == 0 || part.length() > 3) return "Neither";
            if (part.length() > 1 && part.charAt(0) == '0') return "Neither";
            for (char c : part.toCharArray()) {
                if (!Character.isDigit(c)) return "Neither";
            }
            try {
                if (Integer.parseInt(part) > 255) return "Neither";
            } catch (NumberFormatException e) {
                return "Neither";
            }
        }
        return "IPv4";
    }

    private String validateIPv6(String ip) {
        String[] parts = ip.split(":", -1);
        if (parts.length != 8) {
            return "Neither";
        }

        String hexdigits = "0123456789abcdefABCDEF";
        for (String part : parts) {
            if (part.length() == 0 || part.length() > 4) return "Neither";
            for (char c : part.toCharArray()) {
                if (hexdigits.indexOf(c) == -1) return "Neither";
            }
        }
        return "IPv6";
    }
}
```
### Algorithm
- First, determine the potential IP type by counting the delimiters. If the string contains 3 dots, proceed with IPv4 validation. If it contains 7 colons, proceed with IPv6 validation. Otherwise, it's `"Neither"`.
- **For IPv4 validation:**
  - Split the string by `.` into an array of parts. Use a split limit that preserves trailing empty parts.
  - Check if the number of parts is exactly 4. If not, it's invalid.
  - For each part, validate that it:
    1.  Has a length between 1 and 3.
    2.  Does not have a leading zero (unless the part is just `"0"`).
    3.  Contains only digit characters.
    4.  Represents an integer value between 0 and 255.
  - If all parts are valid, return `"IPv4"`.
- **For IPv6 validation:**
  - Split the string by `:` into an array of parts.
  - Check if the number of parts is exactly 8. If not, it's invalid.
  - For each part, validate that it:
    1.  Has a length between 1 and 4.
    2.  Contains only valid hexadecimal characters (`0-9`, `a-f`, `A-F`).
  - If all parts are valid, return `"IPv6"`.
- If any check fails during the process, the string is invalid, and we return `"Neither"`.

# Solutions
### Java

```java
class Solution {
public
  String validIPAddress(String queryIP) {
    if (isIPv4(queryIP)) {
      return "IPv4";
    }
    if (isIPv6(queryIP)) {
      return "IPv6";
    }
    return "Neither";
  }
private
  boolean isIPv4(String s) {
    if (s.endsWith(".")) {
      return false;
    }
    String[] ss = s.split("\\.");
    if (ss.length != 4) {
      return false;
    }
    for (String t : ss) {
      if (t.length() == 0 || t.length() > 1 && t.charAt(0) == '0') {
        return false;
      }
      int x = convert(t);
      if (x < 0 || x > 255) {
        return false;
      }
    }
    return true;
  }
private
  boolean isIPv6(String s) {
    if (s.endsWith(":")) {
      return false;
    }
    String[] ss = s.split(":");
    if (ss.length != 8) {
      return false;
    }
    for (String t : ss) {
      if (t.length() < 1 || t.length() > 4) {
        return false;
      }
      for (char c : t.toCharArray()) {
        if (!Character.isDigit(c) &&
            !"0123456789abcdefABCDEF".contains(String.valueOf(c))) {
          return false;
        }
      }
    }
    return true;
  }
private
  int convert(String s) {
    int x = 0;
    for (char c : s.toCharArray()) {
      if (!Character.isDigit(c)) {
        return -1;
      }
      x = x * 10 + (c - '0');
      if (x > 255) {
        return x;
      }
    }
    return x;
  }
}

```

### CPP

```cpp
class Solution {
public:
  string validIPAddress(string queryIP) {
    if (isIPv4(queryIP)) {
      return "IPv4";
    }
    if (isIPv6(queryIP)) {
      return "IPv6";
    }
    return "Neither";
  }

private:
  bool isIPv4(const string &s) {
    if (s.empty() || s.back() == '.') {
      return false;
    }
    vector<string> ss = split(s, '.');
    if (ss.size() != 4) {
      return false;
    }
    for (const string &t : ss) {
      if (t.empty() || (t.size() > 1 && t[0] == '0')) {
        return false;
      }
      int x = convert(t);
      if (x < 0 || x > 255) {
        return false;
      }
    }
    return true;
  }
  bool isIPv6(const string &s) {
    if (s.empty() || s.back() == ':') {
      return false;
    }
    vector<string> ss = split(s, ':');
    if (ss.size() != 8) {
      return false;
    }
    for (const string &t : ss) {
      if (t.size() < 1 || t.size() > 4) {
        return false;
      }
      for (char c : t) {
        if (!isxdigit(c)) {
          return false;
        }
      }
    }
    return true;
  }
  int convert(const string &s) {
    int x = 0;
    for (char c : s) {
      if (!isdigit(c)) {
        return -1;
      }
      x = x * 10 + (c - '0');
      if (x > 255) {
        return x;
      }
    }
    return x;
  }
  vector<string> split(const string &s, char delimiter) {
    vector<string> tokens;
    string token;
    istringstream iss(s);
    while (getline(iss, token, delimiter)) {
      tokens.push_back(token);
    }
    return tokens;
  }
};

```

### Python

```python
class Solution:
    def validIPAddress(self, IP: str) -> str: if "." in IP: segments = IP . split(".") if len(segments) != 4: return "Neither" for segment in segments: if (not segment . isdigit() or not 0 <= int(segment) <= 255 or (segment[0] == "0" and len(segment) > 1)): return "Neither" return "IPv4" elif ":" in IP: segments = IP . split(":") if len(segments) != 8: return "Neither" for segment in segments: if (not segment or len(segment) > 4 or not all(c in string . hexdigits for c in segment)): return "Neither" return "IPv6" return "Neither"

```
