Skip to content

Commit ac67360

Browse files
committed
Day 34: validate IP address
1 parent ae1e032 commit ac67360

File tree

2 files changed

+127
-0
lines changed

2 files changed

+127
-0
lines changed

day34/README.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
Question of the day: https://leetcode.com/problems/validate-ip-address/#/description
2+
3+
Write a function to check whether an input string is a valid IPv4 address or IPv6 address or neither.
4+
5+
IPv4 addresses are canonically represented in dot-decimal notation, which consists of four decimal numbers, each ranging from 0 to 255, separated by dots ("."), e.g.,172.16.254.1;
6+
7+
Besides, leading zeros in the IPv4 is invalid. For example, the address 172.16.254.01 is invalid.
8+
9+
IPv6 addresses are represented as eight groups of four hexadecimal digits, each group representing 16 bits. The groups are separated by colons (":"). For example, the address 2001:0db8:85a3:0000:0000:8a2e:0370:7334 is a valid one. Also, we could omit some leading zeros among four hexadecimal digits and some low-case characters in the address to upper-case ones, so 2001:db8:85a3:0:0:8A2E:0370:7334 is also a valid IPv6 address(Omit leading zeros and using upper cases).
10+
11+
However, we don't replace a consecutive group of zero value with a single empty group using two consecutive colons (::) to pursue simplicity. For example, 2001:0db8:85a3::8A2E:0370:7334 is an invalid IPv6 address.
12+
13+
Besides, extra leading zeros in the IPv6 is also invalid. For example, the address 02001:0db8:85a3:0000:0000:8a2e:0370:7334 is invalid.
14+
15+
Note: You may assume there is no extra space or special characters in the input string.
16+
17+
## Ideas
18+
19+
Distinguishing between IPv4 and IPv6 is easy: one uses '.' and one uses ':' as
20+
the delimeter. Within the IPv4 addresses, the only constraints I need to validate
21+
are that each number between the periods is an integer between 0-255 inclusive,
22+
and that there are no leading 0s.
23+
24+
IPv4
25+
* '.' delimeter
26+
* each segement is 0-255
27+
* no leading 0's
28+
29+
IPv6 addresses have a little more going on. Each segment is 16 bits, which is
30+
at most 4 digits in hexadecimal. And since they're hexadecimal digits,
31+
letters (a-f) are involved and can be upper case. Leading zero's may or may
32+
not be used, and there are never any empty segments.
33+
34+
IPv6
35+
* ':' delimeter
36+
* each segment has at most 4 digits
37+
* each segment may use letters a, b, c, d, e, f lowercase or capitalized
38+
* a segment with leading 0's may omit the 0's entirely
39+
* a segment that contains only 0's may be represented with a single 0, but not as empty
40+
41+
Input strings that don't fit either set of constraints are neither IPv4 nor IPv6.
42+
43+
This solution basically just parses through the input string a constant number
44+
of times, so it runs in `O(n)` time and `O(1)` space.
45+
46+
## Code
47+
48+
[Python](./validateIP.py)
49+
50+
## Follow-up
51+
52+
53+

day34/validateIP.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
def validateIP(ip):
2+
ipv4 = ip.split(".")
3+
ipv6 = ip.split(":")
4+
if len(ipv4) == 4 and validateIPv4(ipv4):
5+
return "IPv4"
6+
elif len(ipv6) == 8 and validateIPv6(ipv6):
7+
return "IPv6"
8+
else:
9+
return "Neither"
10+
11+
def validateIPv4(segments):
12+
digits = set("1234567890")
13+
for s in segments:
14+
# segment too long or too short
15+
if len(s) > 3 or len(s) == 0:
16+
return False
17+
18+
# segment has leading 0
19+
if len(s) > 1 and s[0] == "0":
20+
return False
21+
22+
# segment contains non-numeric digits
23+
if not set(s) < digits:
24+
return False
25+
26+
# segment is not an int between 0 and 255 inclusive
27+
if int(s) > 255 or int(s) < 0:
28+
return False
29+
return True
30+
31+
def validateIPv6(segments):
32+
hexDigits = set("1234567890abcdefABCDEF")
33+
for s in segments:
34+
# segment too long or too short
35+
if len(s) > 4 or len(s) == 0:
36+
return False
37+
38+
# segment contains non-hexadecimal digits
39+
if not set(s) < hexDigits:
40+
return False
41+
return True
42+
43+
def testValidateIPv4():
44+
assert validateIPv4(["172", "16", "254", "1"])
45+
assert not validateIPv4(["172", "16", "254", "01"])
46+
assert not validateIPv4(["172", "16", "256", "1"])
47+
assert not validateIPv4(["1e1", "4", "5", "6"])
48+
assert not validateIPv4(["1e1", "", "5", "6"])
49+
50+
def testValidateIPv6():
51+
assert validateIPv6(["2001", "0db8", "85a3", "0000", "0000", "8a2e", "0370", "7334"])
52+
assert validateIPv6(["2001", "db8", "85a3", "0", "0", "8A2E", "0370", "7334"])
53+
assert not validateIPv6(["2001", "0db8", "85a3", "", "", "8A2E", "0370", "7334"])
54+
assert not validateIPv6(["02001", "0db8", "85a3", "0000", "0000", "8a2e", "0370", "7334"])
55+
assert not validateIPv6(["GGGG", "0db8", "85a3", "0000", "0000", "8a2e", "0370", "7334"])
56+
57+
def testValidateIP():
58+
assert validateIP("172.16.254.1") == "IPv4"
59+
assert validateIP("172.16.254.01") == "Neither"
60+
assert validateIP("172.16.254.01") == "Neither"
61+
assert validateIP("172.16.256.1") == "Neither"
62+
63+
assert validateIP("2001:0db8:85a3:0000:0000:8a2e:0370:7334") == "IPv6"
64+
assert validateIP("2001:db8:85a3:0:0:8A2E:0370:7334") == "IPv6"
65+
assert validateIP("2001:0db8:85a3:::8A2E:0370:7334") == "Neither"
66+
assert validateIP("02001:db8:85a3:0:0:8A2E:0370:7334") == "Neither"
67+
68+
def main():
69+
testValidateIPv4()
70+
testValidateIPv6()
71+
testValidateIP()
72+
73+
if __name__ == "__main__":
74+
main()

0 commit comments

Comments
 (0)