Evaluate IP Access Rules
Company: Amazon
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: hard
Interview Round: Technical Screen
Quick Answer: This question evaluates knowledge of IPv4 addressing, CIDR subnet matching, and access-control rule resolution, assessing competency in bitwise address reasoning, prefix-length precedence, and rule tie-breaking.
Constraints
- 0 <= number of rules <= 10^5
- Each CIDR prefix length is in the range 0 to 32
- All IPs and CIDR blocks are valid IPv4 (no IPv6)
- action is exactly "allow" or "deny"
Examples
Input: ([['allow', '192.168.0.0/16'], ['deny', '192.168.1.0/24']], '192.168.1.25')
Expected Output: 'deny'
Explanation: Both rules match. /24 is a longer prefix than /16, so the deny rule wins.
Input: ([['deny', '0.0.0.0/0'], ['allow', '10.0.0.0/8']], '10.1.2.3')
Expected Output: 'allow'
Explanation: The /0 default deny and the /8 allow both match; the longer /8 prefix wins -> allow.
Input: ([['allow', '192.168.1.0/24'], ['deny', '192.168.1.0/24']], '192.168.1.7')
Expected Output: 'deny'
Explanation: Same prefix length (/24); the later rule (deny) overrides the earlier allow.
Input: ([], '8.8.8.8')
Expected Output: 'deny'
Explanation: No rules match, so the default decision is deny.
Input: ([['allow', '172.16.0.0/12']], '10.0.0.1')
Expected Output: 'deny'
Explanation: 10.0.0.1 is not inside 172.16.0.0/12, so no rule matches -> deny.
Input: ([['allow', '0.0.0.0/0']], '255.255.255.255')
Expected Output: 'allow'
Explanation: The /0 rule matches every address.
Input: ([['deny', '192.168.0.0/16'], ['allow', '192.168.5.0/24'], ['deny', '192.168.5.128/25']], '192.168.5.130')
Expected Output: 'deny'
Explanation: All three match; the most specific is the /25 deny block, which contains .130 (>= .128).
Input: ([['deny', '192.168.0.0/16'], ['allow', '192.168.5.0/24'], ['deny', '192.168.5.128/25']], '192.168.5.10')
Expected Output: 'allow'
Explanation: .10 is not in the .128/25 half, so the longest matching rule is the /24 allow.
Input: ([['allow', '192.168.1.25/32']], '192.168.1.25')
Expected Output: 'allow'
Explanation: An exact /32 host match -> allow.
Input: ([['allow', '192.168.1.24/32']], '192.168.1.25')
Expected Output: 'deny'
Explanation: .25 differs from the /32 host .24, so it does not match -> default deny.
Hints
- Convert each dotted-quad IPv4 address to a 32-bit integer so containment becomes a bitmask test.
- For a prefix length p, build the mask ((1 << p) - 1) << (32 - p); a rule matches when (target & mask) == (network & mask). Handle p == 0 specially (mask = 0, matches everything).
- Track the longest prefix length seen so far. Use '>=' (not '>') when comparing so that a rule with an equal prefix length that appears later overrides the earlier one.