468. Validate IP Address
Problem Description
You need to determine if a given string queryIP
represents a valid IP address. The function should return:
"IPv4"
if it's a valid IPv4 address"IPv6"
if it's a valid IPv6 address"Neither"
if it's not a valid IP address of either type
IPv4 Address Rules:
- Must have exactly 4 segments separated by dots (
.
) - Format:
"x1.x2.x3.x4"
where eachxi
is a decimal number - Each segment must be between
0
and255
(inclusive) - Leading zeros are NOT allowed (except for the number
0
itself) - Examples:
- Valid:
"192.168.1.1"
,"192.168.1.0"
- Invalid:
"192.168.01.1"
(leading zero),"192.168.1.00"
(leading zeros),"192.168@1.1"
(invalid character)
- Valid:
IPv6 Address Rules:
- Must have exactly 8 segments separated by colons (
:
) - Format:
"x1:x2:x3:x4:x5:x6:x7:x8"
where eachxi
is a hexadecimal string - Each segment must have 1 to 4 characters
- Valid characters are digits (
0-9
), lowercase letters (a-f
), and uppercase letters (A-F
) - Leading zeros ARE allowed in IPv6
- Examples:
- Valid:
"2001:0db8:85a3:0000:0000:8a2e:0370:7334"
,"2001:db8:85a3:0:0:8A2E:0370:7334"
- Invalid:
"2001:0db8:85a3::8A2E:037j:7334"
(contains::
and invalid characterj
),"02001:0db8:85a3:0000:0000:8a2e:0370:7334"
(first segment has 5 characters)
- Valid:
The solution involves creating two helper functions to validate IPv4 and IPv6 formats separately, checking all the specific rules for each format, and returning the appropriate result based on which validation passes.
Intuition
The key insight is that IPv4 and IPv6 have completely different formats and validation rules, so we should handle them separately rather than trying to create a unified validation approach.
We can determine which type to check by looking at the separators in the string:
- IPv4 uses dots (
.
) as separators - IPv6 uses colons (
:
) as separators
Since a valid IP address can only be one type or neither, we can use a straightforward approach:
- Try to validate as IPv4
- If that fails, try to validate as IPv6
- If both fail, return "Neither"
For IPv4 validation, we need to check:
- Split by
.
should give exactly 4 parts - Each part must be a valid decimal number (0-255)
- No leading zeros allowed (except "0" itself)
The tricky part with IPv4 is the leading zero rule. A string like "01"
is invalid, but "0"
is valid. We can check this by seeing if the length is greater than 1 and starts with "0"
.
For IPv6 validation, we need to check:
- Split by
:
should give exactly 8 parts - Each part must be 1-4 characters long
- Each character must be a valid hexadecimal digit (0-9, a-f, A-F)
IPv6 is actually simpler in some ways because leading zeros are allowed, so we don't need that extra check. We just need to validate that each segment contains only valid hexadecimal characters and has the right length.
By breaking down the problem into two separate validation functions, we make the code cleaner and easier to understand. Each function focuses on one specific format's rules, making it straightforward to implement and debug.
Solution Approach
We implement two separate validation functions: is_ipv4
and is_ipv6
, then check the input string against each one.
IPv4 Validation (is_ipv4
function):
-
Split the string: Use
s.split(".")
to divide the string into segments. If we don't get exactly 4 segments, returnFalse
immediately. -
Validate each segment:
- Check for leading zeros: If
len(t) > 1
andt[0] == "0"
, the segment has invalid leading zeros (like "01" or "001"), so returnFalse
. - Check if it's a valid number: Use
t.isdigit()
to ensure all characters are digits. - Check the range: Convert to integer and verify
0 <= int(t) <= 255
.
- Check for leading zeros: If
-
If all segments pass validation, return
True
.
IPv6 Validation (is_ipv6
function):
-
Split the string: Use
s.split(":")
to divide the string into segments. If we don't get exactly 8 segments, returnFalse
immediately. -
Validate each segment:
- Check length: Each segment must have
1 <= len(t) <= 4
characters. - Check characters: Use
all(c in "0123456789abcdefABCDEF" for c in t)
to ensure every character is a valid hexadecimal digit.
- Check length: Each segment must have
-
If all segments pass validation, return
True
.
Main Function Logic:
if is_ipv4(queryIP): return "IPv4" if is_ipv6(queryIP): return "IPv6" return "Neither"
The order of checking doesn't matter since a valid IP can only be one type. We first check IPv4, then IPv6, and if both fail, we return "Neither".
Key Implementation Details:
- The
isdigit()
method is perfect for IPv4 validation as it returnsTrue
only if all characters are decimal digits. - For IPv6, we explicitly check against the allowed character set
"0123456789abcdefABCDEF"
since hexadecimal includes letters. - The leading zero check for IPv4 is done before the numeric validation to avoid issues with strings like "00" which would pass the range check but are invalid.
- Empty segments would naturally fail our validations (empty string won't pass
isdigit()
for IPv4, and would have length 0 for IPv6).
This approach is straightforward and handles all edge cases by explicitly checking each validation rule defined in the problem.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the solution with two examples to see how the validation works.
Example 1: queryIP = "172.16.254.1"
-
First, we try
is_ipv4("172.16.254.1")
:- Split by
.
:["172", "16", "254", "1"]
- we get exactly 4 segments ✓ - Check segment "172":
- Length > 1 and starts with '0'? No (starts with '1') ✓
- All digits? Yes ✓
- In range [0, 255]? Yes (172 is valid) ✓
- Check segment "16":
- Length > 1 and starts with '0'? No ✓
- All digits? Yes ✓
- In range [0, 255]? Yes ✓
- Check segment "254":
- Length > 1 and starts with '0'? No ✓
- All digits? Yes ✓
- In range [0, 255]? Yes ✓
- Check segment "1":
- Length > 1 and starts with '0'? No (length = 1) ✓
- All digits? Yes ✓
- In range [0, 255]? Yes ✓
- All segments valid, return
True
- Split by
-
Since
is_ipv4
returnedTrue
, we return"IPv4"
Example 2: queryIP = "2001:0db8:85a3:0:0:8A2E:0370:7334"
-
First, we try
is_ipv4("2001:0db8:85a3:0:0:8A2E:0370:7334")
:- Split by
.
:["2001:0db8:85a3:0:0:8A2E:0370:7334"]
- only 1 segment, not 4 ✗ - Return
False
- Split by
-
Next, we try
is_ipv6("2001:0db8:85a3:0:0:8A2E:0370:7334")
:- Split by
:
:["2001", "0db8", "85a3", "0", "0", "8A2E", "0370", "7334"]
- exactly 8 segments ✓ - Check segment "2001":
- Length between 1-4? Yes (length = 4) ✓
- All characters in "0123456789abcdefABCDEF"? Yes ✓
- Check segment "0db8":
- Length between 1-4? Yes (length = 4) ✓
- All characters valid hex? Yes ✓
- Check segment "85a3":
- Length between 1-4? Yes ✓
- All characters valid hex? Yes ✓
- Check segments "0", "0": Both have length 1 and valid hex ✓
- Check segment "8A2E":
- Length between 1-4? Yes ✓
- All characters valid hex? Yes (uppercase A and E are valid) ✓
- Check segments "0370", "7334": Both valid ✓
- All segments valid, return
True
- Split by
-
Since
is_ipv6
returnedTrue
, we return"IPv6"
Example 3: queryIP = "192.168.01.1"
-
First, we try
is_ipv4("192.168.01.1")
:- Split by
.
:["192", "168", "01", "1"]
- exactly 4 segments ✓ - Check segments "192", "168": Both valid ✓
- Check segment "01":
- Length > 1 and starts with '0'? Yes (length = 2, starts with '0') ✗
- This is a leading zero violation, return
False
- Split by
-
Next, we try
is_ipv6("192.168.01.1")
:- Split by
:
:["192.168.01.1"]
- only 1 segment, not 8 ✗ - Return
False
- Split by
-
Both validations failed, so we return
"Neither"
Solution Implementation
1class Solution:
2 def validIPAddress(self, queryIP: str) -> str:
3 """
4 Validates whether a given string is a valid IPv4 or IPv6 address.
5
6 Args:
7 queryIP: String to validate as an IP address
8
9 Returns:
10 "IPv4" if valid IPv4, "IPv6" if valid IPv6, "Neither" otherwise
11 """
12
13 def is_ipv4(ip_string: str) -> bool:
14 """
15 Checks if the string is a valid IPv4 address.
16 IPv4 format: X.X.X.X where X is a number from 0 to 255
17 """
18 # Split by dots - IPv4 should have exactly 4 segments
19 segments = ip_string.split(".")
20 if len(segments) != 4:
21 return False
22
23 for segment in segments:
24 # Check for leading zeros (not allowed except for "0" itself)
25 if len(segment) > 1 and segment[0] == "0":
26 return False
27
28 # Check if segment contains only digits and is in valid range
29 if not segment.isdigit() or not 0 <= int(segment) <= 255:
30 return False
31
32 return True
33
34 def is_ipv6(ip_string: str) -> bool:
35 """
36 Checks if the string is a valid IPv6 address.
37 IPv6 format: X:X:X:X:X:X:X:X where X is a 1-4 digit hexadecimal
38 """
39 # Split by colons - IPv6 should have exactly 8 segments
40 segments = ip_string.split(":")
41 if len(segments) != 8:
42 return False
43
44 for segment in segments:
45 # Each segment should be 1-4 characters long
46 if not 1 <= len(segment) <= 4:
47 return False
48
49 # Check if all characters are valid hexadecimal digits
50 if not all(char in "0123456789abcdefABCDEF" for char in segment):
51 return False
52
53 return True
54
55 # Check which type of IP address it is
56 if is_ipv4(queryIP):
57 return "IPv4"
58 if is_ipv6(queryIP):
59 return "IPv6"
60 return "Neither"
61
1class Solution {
2 /**
3 * Validates whether a given string is a valid IPv4 or IPv6 address
4 * @param queryIP the input string to validate
5 * @return "IPv4" if valid IPv4, "IPv6" if valid IPv6, "Neither" otherwise
6 */
7 public String validIPAddress(String queryIP) {
8 if (isIPv4(queryIP)) {
9 return "IPv4";
10 }
11 if (isIPv6(queryIP)) {
12 return "IPv6";
13 }
14 return "Neither";
15 }
16
17 /**
18 * Checks if the given string is a valid IPv4 address
19 * IPv4 format: x1.x2.x3.x4 where 0 <= xi <= 255 and xi cannot have leading zeros
20 * @param ipString the string to validate
21 * @return true if valid IPv4, false otherwise
22 */
23 private boolean isIPv4(String ipString) {
24 // IPv4 cannot end with a dot
25 if (ipString.endsWith(".")) {
26 return false;
27 }
28
29 // Split by dot separator
30 String[] segments = ipString.split("\\.");
31
32 // IPv4 must have exactly 4 segments
33 if (segments.length != 4) {
34 return false;
35 }
36
37 // Validate each segment
38 for (String segment : segments) {
39 // Check for empty segment or leading zeros (except single "0")
40 if (segment.length() == 0 || (segment.length() > 1 && segment.charAt(0) == '0')) {
41 return false;
42 }
43
44 // Convert to integer and validate range [0, 255]
45 int value = convertToInteger(segment);
46 if (value < 0 || value > 255) {
47 return false;
48 }
49 }
50
51 return true;
52 }
53
54 /**
55 * Checks if the given string is a valid IPv6 address
56 * IPv6 format: x1:x2:x3:x4:x5:x6:x7:x8 where xi is a hexadecimal string (1-4 digits)
57 * @param ipString the string to validate
58 * @return true if valid IPv6, false otherwise
59 */
60 private boolean isIPv6(String ipString) {
61 // IPv6 cannot end with a colon
62 if (ipString.endsWith(":")) {
63 return false;
64 }
65
66 // Split by colon separator
67 String[] segments = ipString.split(":");
68
69 // IPv6 must have exactly 8 segments
70 if (segments.length != 8) {
71 return false;
72 }
73
74 // Validate each segment
75 for (String segment : segments) {
76 // Each segment must be 1-4 characters long
77 if (segment.length() < 1 || segment.length() > 4) {
78 return false;
79 }
80
81 // Validate each character is a valid hexadecimal digit
82 for (char character : segment.toCharArray()) {
83 if (!Character.isDigit(character) &&
84 !"0123456789abcdefABCDEF".contains(String.valueOf(character))) {
85 return false;
86 }
87 }
88 }
89
90 return true;
91 }
92
93 /**
94 * Converts a string to integer, returns -1 if string contains non-digit characters
95 * @param numberString the string to convert
96 * @return the integer value or -1 if invalid
97 */
98 private int convertToInteger(String numberString) {
99 int result = 0;
100
101 for (char character : numberString.toCharArray()) {
102 // Return -1 if non-digit character found
103 if (!Character.isDigit(character)) {
104 return -1;
105 }
106
107 // Build the integer value
108 result = result * 10 + (character - '0');
109
110 // Early return if value exceeds 255
111 if (result > 255) {
112 return result;
113 }
114 }
115
116 return result;
117 }
118}
119
1class Solution {
2public:
3 string validIPAddress(string queryIP) {
4 // Check if the input is a valid IPv4 address
5 if (isIPv4(queryIP)) {
6 return "IPv4";
7 }
8 // Check if the input is a valid IPv6 address
9 if (isIPv6(queryIP)) {
10 return "IPv6";
11 }
12 // Neither IPv4 nor IPv6
13 return "Neither";
14 }
15
16private:
17 bool isIPv4(const string& ipString) {
18 // IPv4 cannot be empty or end with a dot
19 if (ipString.empty() || ipString.back() == '.') {
20 return false;
21 }
22
23 // Split the string by dots
24 vector<string> segments = split(ipString, '.');
25
26 // IPv4 must have exactly 4 segments
27 if (segments.size() != 4) {
28 return false;
29 }
30
31 // Validate each segment
32 for (const string& segment : segments) {
33 // Segment cannot be empty or have leading zeros (except for "0" itself)
34 if (segment.empty() || (segment.size() > 1 && segment[0] == '0')) {
35 return false;
36 }
37
38 // Convert segment to integer and validate range [0, 255]
39 int value = convertToInt(segment);
40 if (value < 0 || value > 255) {
41 return false;
42 }
43 }
44
45 return true;
46 }
47
48 bool isIPv6(const string& ipString) {
49 // IPv6 cannot be empty or end with a colon
50 if (ipString.empty() || ipString.back() == ':') {
51 return false;
52 }
53
54 // Split the string by colons
55 vector<string> segments = split(ipString, ':');
56
57 // IPv6 must have exactly 8 segments
58 if (segments.size() != 8) {
59 return false;
60 }
61
62 // Validate each segment
63 for (const string& segment : segments) {
64 // Each segment must be 1-4 characters long
65 if (segment.size() < 1 || segment.size() > 4) {
66 return false;
67 }
68
69 // Each character must be a valid hexadecimal digit
70 for (char ch : segment) {
71 if (!isxdigit(ch)) {
72 return false;
73 }
74 }
75 }
76
77 return true;
78 }
79
80 int convertToInt(const string& str) {
81 int result = 0;
82
83 // Convert string to integer, checking for non-digit characters
84 for (char ch : str) {
85 if (!isdigit(ch)) {
86 return -1; // Invalid character found
87 }
88
89 result = result * 10 + (ch - '0');
90
91 // Early return if value exceeds 255
92 if (result > 255) {
93 return result;
94 }
95 }
96
97 return result;
98 }
99
100 vector<string> split(const string& str, char delimiter) {
101 vector<string> tokens;
102 string token;
103 istringstream stringStream(str);
104
105 // Split the string by the delimiter using getline
106 while (getline(stringStream, token, delimiter)) {
107 tokens.push_back(token);
108 }
109
110 return tokens;
111 }
112};
113
1/**
2 * Validates if a given string is a valid IPv4 or IPv6 address
3 * @param queryIP - The IP address string to validate
4 * @returns 'IPv4' if valid IPv4, 'IPv6' if valid IPv6, 'Neither' otherwise
5 */
6function validIPAddress(queryIP: string): string {
7 if (isIPv4(queryIP)) {
8 return 'IPv4';
9 }
10 if (isIPv6(queryIP)) {
11 return 'IPv6';
12 }
13 return 'Neither';
14}
15
16/**
17 * Checks if a string is a valid IPv4 address
18 * Valid IPv4: four decimal numbers separated by dots, each between 0-255
19 * No leading zeros allowed (except for '0' itself)
20 * @param s - The string to validate as IPv4
21 * @returns true if valid IPv4, false otherwise
22 */
23function isIPv4(s: string): boolean {
24 // IPv4 cannot end with a dot
25 if (s.endsWith('.')) {
26 return false;
27 }
28
29 // Split by dots and check for exactly 4 segments
30 const segments: string[] = s.split('.');
31 if (segments.length !== 4) {
32 return false;
33 }
34
35 // Validate each segment
36 for (const segment of segments) {
37 // Empty segments are invalid
38 // Segments with leading zeros (except single '0') are invalid
39 if (segment.length === 0 || (segment.length > 1 && segment[0] === '0')) {
40 return false;
41 }
42
43 // Convert to number and validate range [0, 255]
44 const num: number = convert(segment);
45 if (num < 0 || num > 255) {
46 return false;
47 }
48 }
49
50 return true;
51}
52
53/**
54 * Checks if a string is a valid IPv6 address
55 * Valid IPv6: eight hexadecimal groups separated by colons
56 * Each group is 1-4 hexadecimal digits
57 * @param s - The string to validate as IPv6
58 * @returns true if valid IPv6, false otherwise
59 */
60function isIPv6(s: string): boolean {
61 // IPv6 cannot end with a colon
62 if (s.endsWith(':')) {
63 return false;
64 }
65
66 // Split by colons and check for exactly 8 segments
67 const segments: string[] = s.split(':');
68 if (segments.length !== 8) {
69 return false;
70 }
71
72 // Validate each segment
73 for (const segment of segments) {
74 // Each segment must be 1-4 characters long
75 if (segment.length < 1 || segment.length > 4) {
76 return false;
77 }
78
79 // Each character must be a hexadecimal digit
80 for (const char of segment) {
81 if (!isHexDigit(char)) {
82 return false;
83 }
84 }
85 }
86
87 return true;
88}
89
90/**
91 * Converts a string to a number, validating that all characters are digits
92 * @param s - The string to convert
93 * @returns The numeric value if valid, -1 if invalid
94 */
95function convert(s: string): number {
96 let result: number = 0;
97
98 for (const char of s) {
99 // Return -1 if any character is not a digit
100 if (!isDigit(char)) {
101 return -1;
102 }
103
104 // Build the number digit by digit
105 result = result * 10 + (char.charCodeAt(0) - '0'.charCodeAt(0));
106
107 // Early return if number exceeds 255
108 if (result > 255) {
109 return result;
110 }
111 }
112
113 return result;
114}
115
116/**
117 * Checks if a character is a decimal digit (0-9)
118 * @param c - The character to check
119 * @returns true if the character is a digit, false otherwise
120 */
121function isDigit(c: string): boolean {
122 return c >= '0' && c <= '9';
123}
124
125/**
126 * Checks if a character is a hexadecimal digit (0-9, a-f, A-F)
127 * @param c - The character to check
128 * @returns true if the character is a hex digit, false otherwise
129 */
130function isHexDigit(c: string): boolean {
131 return (c >= '0' && c <= '9') ||
132 (c >= 'a' && c <= 'f') ||
133 (c >= 'A' && c <= 'F');
134}
135
Time and Space Complexity
Time Complexity: O(n)
The algorithm processes the input string queryIP
of length n
in the following ways:
- The
split()
operation on either "." or ":" traverses the entire string once, takingO(n)
time - For IPv4 validation: iterating through at most 4 segments, each with constant operations like
isdigit()
and integer conversion on segments of bounded size (max 3 digits) - For IPv6 validation: iterating through at most 8 segments, checking each character against the valid hexadecimal set, but the total characters checked across all segments is bounded by
n
- The character-by-character validation in IPv6 (
all(c in "0123456789abcdefABCDEF" for c in t)
) processes each character in the string at most once
Since each character in the input string is processed a constant number of times, the overall time complexity is O(n)
.
Space Complexity: O(n)
The space usage comes from:
- The
split()
operation creates a new list containing segments of the original string. In the worst case, this stores all characters from the input string in the list format, requiringO(n)
space - The string segments
t
are references to portions of the split result, not creating additional copies - All other variables (
ss
, loop variables) use constant space
Therefore, the space complexity is O(n)
where n
is the length of the input string queryIP
.
Common Pitfalls
1. Not Handling Empty Segments Properly
A common mistake is not checking for empty segments that can occur with consecutive delimiters or trailing delimiters.
Problematic scenarios:
"192.168.1."
- trailing dot creates an empty segment"192..168.1.1"
- consecutive dots create an empty segment":2001:db8:85a3:0:0:8A2E:0370:7334"
- leading colon creates an empty segment
Why the current solution handles it correctly:
- For IPv4:
segment.isdigit()
returnsFalse
for empty strings - For IPv6: The length check
1 <= len(segment) <= 4
fails for empty segments
2. Integer Overflow When Converting IPv4 Segments
When a segment contains a very large number like "99999999999999999999"
, calling int(segment)
could theoretically cause issues in some languages, though Python handles arbitrary precision integers well.
Better approach for robustness:
# Add a length check before conversion
for segment in segments:
# Limit segment length to prevent unnecessarily large numbers
if len(segment) > 3: # Max valid IPv4 segment is "255" (3 digits)
return False
if len(segment) > 1 and segment[0] == "0":
return False
if not segment.isdigit() or not 0 <= int(segment) <= 255:
return False
3. Case Sensitivity Confusion in IPv6
Developers might forget that IPv6 accepts both uppercase and lowercase hexadecimal letters.
Common mistake:
# Wrong - only checks lowercase
if not all(char in "0123456789abcdef" for char in segment):
return False
Correct approach (as in the solution):
# Includes both cases
if not all(char in "0123456789abcdefABCDEF" for char in segment):
return False
4. Incorrect Leading Zero Validation for IPv4
A subtle bug occurs when checking for leading zeros without considering single "0" as valid.
Common mistake:
# Wrong - rejects valid "0" segments if segment[0] == "0": return False
Correct approach (as in the solution):
# Only reject if length > 1 AND starts with "0"
if len(segment) > 1 and segment[0] == "0":
return False
5. Not Validating Before Integer Conversion
Attempting to convert non-numeric strings to integers will raise an exception.
Common mistake:
# Wrong - will crash on "192.168.1.1a"
for segment in segments:
if not 0 <= int(segment) <= 255: # ValueError if segment contains letters
return False
Correct approach (as in the solution):
# Check if it's numeric first
if not segment.isdigit() or not 0 <= int(segment) <= 255:
return False
These pitfalls highlight the importance of thorough input validation and careful attention to edge cases when parsing structured string formats like IP addresses.
Which of the following is a good use case for backtracking?
Recommended Readings
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!