Skip to content

Commit 322dace

Browse files
author
Albert Hu
authored
Merge pull request #23 from alberthu16/day34
Day 34: validate IP address
2 parents ede96c1 + c84e4b0 commit 322dace

File tree

2 files changed

+129
-0
lines changed

2 files changed

+129
-0
lines changed

day34/README.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
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 but because I use the python split
45+
function, it's also using and `O(n)` space.
46+
47+
## Code
48+
49+
[Python](./validateIP.py)
50+
51+
## Follow-up
52+
53+
We can reduce the linear time complexity by iterating over the entire input
54+
string once to find whether the string is an ipv4 or ipv6 candidate, and then
55+
doing the check on the string via iteration without the `.split(".")` or
56+
`.split(":")` calls. The iteration syntax may be a little messy though.
57+

day34/validateIP.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import re
2+
3+
def validateIP(ip):
4+
ipv4 = ip.split(".")
5+
ipv6 = ip.split(":")
6+
if len(ipv4) == 4 and isValidIPv4(ip):
7+
return "IPv4"
8+
elif len(ipv6) == 8 and isValidIPv6(ip):
9+
return "IPv6"
10+
else:
11+
return "Neither"
12+
13+
def isValidIPv4(ip):
14+
for s in ip.split("."):
15+
numDigits = len(s)
16+
17+
if not 1 <= numDigits <= 3:
18+
return False
19+
20+
if s.startswith("0"):
21+
return False
22+
23+
if not re.match("[1234567890]{{{}}}".format(numDigits), s):
24+
return False
25+
26+
if not 0 <= int(s) <= 255:
27+
return False
28+
return True
29+
30+
def isValidIPv6(ip):
31+
for s in ip.split(":"):
32+
numDigits = len(s)
33+
34+
if not 1 <= numDigits <= 4:
35+
return False
36+
37+
if not re.match("[1234567890abcdefABCDEF]{{{}}}".format(numDigits), s):
38+
return False
39+
return True
40+
41+
def testIsValidIPv4():
42+
assert isValidIPv4("172.16.254.1")
43+
assert not isValidIPv4("172.16.254.01")
44+
assert not isValidIPv4("172.16.256.1")
45+
assert not isValidIPv4("1e1.4.5.6")
46+
assert not isValidIPv4("1e1..5.6")
47+
48+
def testIsValidIPv6():
49+
assert isValidIPv6("2001:0db8:85a3:0000:0000:8a2e:0370:7334")
50+
assert isValidIPv6("2001:db8:85a3:0:0:8A2E:0370:7334")
51+
assert not isValidIPv6("2001:0db8:85a3:::8A2E:0370:7334")
52+
assert not isValidIPv6("02001:0db8:85a3:0000:0000:8a2e:0370:7334")
53+
assert not isValidIPv6("GGGG:0db8:85a3:0000:0000:8a2e:0370:7334")
54+
55+
def testValidateIP():
56+
assert validateIP("172.16.254.1") == "IPv4"
57+
assert validateIP("172.16.254.01") == "Neither"
58+
assert validateIP("172.16.254.01") == "Neither"
59+
assert validateIP("172.16.256.1") == "Neither"
60+
61+
assert validateIP("2001:0db8:85a3:0000:0000:8a2e:0370:7334") == "IPv6"
62+
assert validateIP("2001:db8:85a3:0:0:8A2E:0370:7334") == "IPv6"
63+
assert validateIP("2001:0db8:85a3:::8A2E:0370:7334") == "Neither"
64+
assert validateIP("02001:db8:85a3:0:0:8A2E:0370:7334") == "Neither"
65+
66+
def main():
67+
testIsValidIPv4()
68+
testIsValidIPv6()
69+
testValidateIP()
70+
71+
if __name__ == "__main__":
72+
main()

0 commit comments

Comments
 (0)