Design a coupon pricing engine
Company: Applied Intuition
Role: Software Engineer
Category: Coding & Algorithms
Difficulty: Medium
Interview Round: Technical Screen
Quick Answer: This question evaluates software design and algorithmic problem-solving skills focused on pricing logic, including handling financial rounding, discount ordering, eligibility criteria across item categories, and time/space complexity analysis.
Part 1: Apply One Coupon to a Single Category
Constraints
- 0 <= len(items) <= 200000
- 0 <= unit_price <= 10^9
- 0 <= minimum_total_item_requirement <= len(items)
- 0 <= minimum_total_price_requirement <= 10^14
- 0 <= percentage_discount <= 100
- 0 <= value_discount <= 10^14
- All prices are in cents.
Examples
Input: ([{'id': 1, 'category': 'A', 'unit_price': 1000}, {'id': 2, 'category': 'A', 'unit_price': 2000}, {'id': 3, 'category': 'B', 'unit_price': 500}], {'applicable_category': 'A', 'minimum_total_item_requirement': 2, 'minimum_total_price_requirement': 2500, 'percentage_discount': 10, 'value_discount': 300})
Expected Output: 2900
Explanation: Eligible A subtotal is 3000. Apply 10% off => 300 cents discount, then 300 cents flat discount, so A items become 2400. Add B item 500 => 2900.
Input: ([{'id': 1, 'category': 'A', 'unit_price': 999}], {'applicable_category': 'A', 'minimum_total_item_requirement': 1, 'minimum_total_price_requirement': 0, 'percentage_discount': 15, 'value_discount': 0})
Expected Output: 849
Explanation: 15% of 999 is 149.85 cents, which rounds half up to 150 cents. Final total is 999 - 150 = 849.
Input: ([{'id': 1, 'category': 'A', 'unit_price': 1000}, {'id': 2, 'category': 'B', 'unit_price': 500}], {'applicable_category': 'A', 'minimum_total_item_requirement': 2, 'minimum_total_price_requirement': 1000, 'percentage_discount': 20, 'value_discount': 100})
Expected Output: 1500
Explanation: Only one eligible A item exists, so the minimum item requirement is not met. No discount is applied.
Input: ([{'id': 1, 'category': 'A', 'unit_price': 500}, {'id': 2, 'category': 'B', 'unit_price': 700}], {'applicable_category': 'A', 'minimum_total_item_requirement': 1, 'minimum_total_price_requirement': 0, 'percentage_discount': 50, 'value_discount': 400})
Expected Output: 700
Explanation: Eligible subtotal is 500. After 50% off, it becomes 250. Subtract 400 more, clamp at 0. Final total is only the B item: 700.
Input: ([], {'applicable_category': 'A', 'minimum_total_item_requirement': 1, 'minimum_total_price_requirement': 0, 'percentage_discount': 10, 'value_discount': 100})
Expected Output: 0
Explanation: Empty cart edge case.
Hints
- Compute the full cart total and the eligible subtotal separately.
- For half-up rounding in cents, the percentage discount on subtotal S with percent P can be computed as (S * P + 50) // 100.
Part 2: Multi-Category Coupon Engine with Overlapping Coupons
Constraints
- 0 <= len(items) <= 200000
- 0 <= len(coupons) <= 200000
- 1 <= total number of category names across all coupons <= 200000, for non-empty coupon lists
- 0 <= unit_price <= 10^9
- 0 <= minimum_total_item_requirement <= len(items)
- 0 <= minimum_total_price_requirement <= 10^14
- 0 <= percentage_discount <= 100
- 0 <= value_discount <= 10^14
- All prices are in cents.
Examples
Input: ([{'id': 1, 'category': 'grocery', 'unit_price': 500}, {'id': 2, 'category': 'beauty', 'unit_price': 300}, {'id': 3, 'category': 'toy', 'unit_price': 200}], [{'applicable_categories': ['grocery', 'beauty'], 'minimum_total_item_requirement': 2, 'minimum_total_price_requirement': 700, 'percentage_discount': 10, 'value_discount': 100}])
Expected Output: 820
Explanation: The coupon combines grocery and beauty items for subtotal 800. After 10% off and then 100 cents flat off, they become 620. Add toy item 200 => 820.
Input: ([{'id': 1, 'category': 'A', 'unit_price': 1000}, {'id': 2, 'category': 'B', 'unit_price': 600}, {'id': 3, 'category': 'B', 'unit_price': 400}, {'id': 4, 'category': 'C', 'unit_price': 500}], [{'applicable_categories': ['A', 'B'], 'minimum_total_item_requirement': 3, 'minimum_total_price_requirement': 1800, 'percentage_discount': 10, 'value_discount': 100}, {'applicable_categories': ['B', 'C'], 'minimum_total_item_requirement': 1, 'minimum_total_price_requirement': 0, 'percentage_discount': 20, 'value_discount': 0}])
Expected Output: 2100
Explanation: First coupon uses all remaining A and B items: 2000 -> 1700 after discounts. Those items are consumed. Second coupon can only use C, so 500 -> 400. Final total is 1700 + 400 = 2100.
Input: ([{'id': 1, 'category': 'A', 'unit_price': 400}, {'id': 2, 'category': 'B', 'unit_price': 400}], [{'applicable_categories': ['A', 'B'], 'minimum_total_item_requirement': 3, 'minimum_total_price_requirement': 0, 'percentage_discount': 50, 'value_discount': 0}, {'applicable_categories': ['A'], 'minimum_total_item_requirement': 1, 'minimum_total_price_requirement': 0, 'percentage_discount': 25, 'value_discount': 0}])
Expected Output: 700
Explanation: The first coupon is skipped because it needs 3 eligible items but only 2 are available. The second coupon then discounts category A from 400 to 300. Category B stays 400.
Input: ([{'id': 1, 'category': 'A', 'unit_price': 300}, {'id': 2, 'category': 'B', 'unit_price': 200}], [{'applicable_categories': ['A', 'B', 'A'], 'minimum_total_item_requirement': 2, 'minimum_total_price_requirement': 0, 'percentage_discount': 50, 'value_discount': 400}])
Expected Output: 0
Explanation: Duplicate 'A' should not double count. Eligible subtotal is 500, then 50% off gives 250, then subtract 400 and clamp at 0.
Input: ([{'id': 1, 'category': 'X', 'unit_price': 500}], [{'applicable_categories': ['A', 'B'], 'minimum_total_item_requirement': 1, 'minimum_total_price_requirement': 0, 'percentage_discount': 10, 'value_discount': 100}])
Expected Output: 500
Explanation: No eligible items match the coupon categories, so the cart total is unchanged.
Hints
- Aggregate the remaining item count and subtotal by category so each coupon can be evaluated without rescanning every item.
- When a coupon succeeds, all remaining items in its listed categories are consumed, so their category aggregates can be zeroed out.