1357. Apply Discount Every n Orders
Problem Description
You need to implement a cashier system for a supermarket that tracks products and applies discounts to certain customers.
The supermarket has products with unique IDs and corresponding prices. These are given as two arrays:
products[i]
represents the ID of the i-th productprices[i]
represents the price of the i-th product
When customers check out, their purchases are represented by:
product[j]
- the ID of the j-th product they're buyingamount[j]
- the quantity of the j-th product they're buying
The subtotal is calculated by summing up amount[j] * price
for each product.
The supermarket runs a special promotion where every n-th customer receives a percentage discount. For example, if n = 3
and discount = 50
, then the 3rd, 6th, 9th, etc. customers get 50% off their subtotal.
You need to implement a Cashier
class with two methods:
-
Constructor
Cashier(n, discount, products, prices)
: Initializes the cashier system with:n
: Every n-th customer gets the discountdiscount
: The discount percentage (0-100)products
andprices
: Arrays defining the store's inventory
-
getBill
(product, amount)
: Calculates and returns the final bill for a customer:- Takes arrays of product IDs and their quantities
- Applies the discount if this is the n-th customer
- Returns the final total as a float
- The formula for discount is:
final_price = subtotal * (100 - discount) / 100
The system needs to keep track of how many customers have been served to determine who gets the discount. Answers within 10^-5
of the actual value are accepted.
Intuition
The key insight is that we need to efficiently look up product prices and keep track of customer count to apply discounts at the right time.
Since we need to frequently look up prices by product ID when calculating bills, a hash table (dictionary) is the natural choice. This gives us O(1)
price lookups instead of searching through the products array each time.
For tracking which customer gets the discount, we need a counter that increments with each customer. When counter % n == 0
, we know this customer should receive the discount. This is a simple modulo operation that tells us if the current customer number is divisible by n
.
The calculation flow becomes straightforward:
- Increment the customer counter
- Check if this customer gets a discount using
counter % n
- Calculate the subtotal by looking up each product's price in our hash table and multiplying by quantity
- If there's a discount, apply it to each item as we calculate
The discount calculation can be done in two ways:
- Calculate the full subtotal first, then apply discount:
subtotal * (100 - discount) / 100
- Apply discount to each item individually:
price * amount * (100 - discount) / 100
Both approaches work, but applying the discount item by item as shown in the solution allows us to combine the calculation in a single pass through the products. The formula x - (discount * x) / 100
is mathematically equivalent to x * (100 - discount) / 100
, where x
is the item's cost before discount.
Solution Approach
The solution uses a hash table combined with simulation to efficiently handle product lookups and customer tracking.
Data Structure Setup:
- Create a hash table
d
that maps each product ID to its price:{product_id: price}
- Initialize a counter
i
to track the number of customers served - Store the discount parameters
n
anddiscount
for later use
Implementation Details:
-
Constructor (
__init__
):- Set
self.i = 0
to track customer count - Store
n
anddiscount
values - Build the hash table using dictionary comprehension:
self.d = {product: price for product, price in zip(products, prices)}
- The
zip
function pairs each product ID with its corresponding price
- Set
-
Bill Calculation (
getBill
):- Increment the customer counter:
self.i += 1
- Determine if current customer gets discount:
discount = self.discount if self.i % self.n == 0 else 0
- If
i % n == 0
, this is the n-th customer and gets the discount - Otherwise, discount is 0
- If
- Calculate the total bill:
ans = 0 for p, a in zip(product, amount): x = self.d[p] * a # price * quantity ans += x - (discount * x) / 100 # apply discount if any
- The formula
x - (discount * x) / 100
calculates the discounted price- When
discount = 0
: result isx
(no discount) - When
discount > 0
: result isx * (1 - discount/100)
(discounted price)
- When
- Increment the customer counter:
Time Complexity:
- Constructor:
O(m)
wherem
is the number of products getBill
:O(k)
wherek
is the number of items in the current purchase
Space Complexity:
O(m)
to store the hash table of products and prices
Ready to land your dream job?
Unlock your dream job with a 3-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a concrete example to illustrate how the cashier system works.
Setup:
- Initialize:
Cashier(n=3, discount=50, products=[1,2,3,4,5], prices=[100,200,300,400,500])
- This creates a hash table:
{1:100, 2:200, 3:300, 4:400, 5:500}
- Customer counter starts at
i=0
- Every 3rd customer gets 50% off
Customer 1:
- Call:
getBill([1,2], [1,2])
- Counter increments:
i=1
- Check discount:
1 % 3 = 1
(not divisible by 3, no discount) - Calculate bill:
- Product 1:
100 * 1 = 100
- Product 2:
200 * 2 = 400
- Total:
100 + 400 = 500.0
- Product 1:
Customer 2:
- Call:
getBill([3,5], [2,1])
- Counter increments:
i=2
- Check discount:
2 % 3 = 2
(no discount) - Calculate bill:
- Product 3:
300 * 2 = 600
- Product 5:
500 * 1 = 500
- Total:
600 + 500 = 1100.0
- Product 3:
Customer 3 (Gets Discount!):
- Call:
getBill([1,2], [1,2])
- Counter increments:
i=3
- Check discount:
3 % 3 = 0
(divisible by 3, apply 50% discount) - Calculate bill with discount:
- Product 1:
100 * 1 = 100
, after discount:100 - (50 * 100)/100 = 50
- Product 2:
200 * 2 = 400
, after discount:400 - (50 * 400)/100 = 200
- Total:
50 + 200 = 250.0
(half of the original 500)
- Product 1:
Customer 4:
- Call:
getBill([4], [3])
- Counter increments:
i=4
- Check discount:
4 % 3 = 1
(no discount) - Calculate bill:
- Product 4:
400 * 3 = 1200
- Total:
1200.0
- Product 4:
Customer 6 (Gets Discount!):
- Would get 50% off since
6 % 3 = 0
This example demonstrates how the hash table provides instant price lookups and the modulo operation efficiently determines which customers receive discounts.
Solution Implementation
1from typing import List
2
3class Cashier:
4 def __init__(self, n: int, discount: int, products: List[int], prices: List[int]):
5 """
6 Initialize the Cashier system.
7
8 Args:
9 n: Every nth customer will receive the discount
10 discount: Discount percentage to apply
11 products: List of product IDs
12 prices: List of corresponding product prices
13 """
14 # Counter to track the number of customers
15 self.customer_count = 0
16 # Every nth customer gets a discount
17 self.n = n
18 # Discount percentage
19 self.discount = discount
20 # Dictionary mapping product ID to its price
21 self.product_price_map = {product_id: price
22 for product_id, price in zip(products, prices)}
23
24 def getBill(self, product: List[int], amount: List[int]) -> float:
25 """
26 Calculate the bill for a customer's purchase.
27
28 Args:
29 product: List of product IDs being purchased
30 amount: List of quantities for each product
31
32 Returns:
33 Total bill amount after applying discount (if applicable)
34 """
35 # Increment customer counter
36 self.customer_count += 1
37
38 # Check if current customer is eligible for discount (every nth customer)
39 discount_percentage = self.discount if self.customer_count % self.n == 0 else 0
40
41 # Calculate total bill
42 total_bill = 0.0
43 for product_id, quantity in zip(product, amount):
44 # Calculate subtotal for this product
45 subtotal = self.product_price_map[product_id] * quantity
46 # Apply discount if eligible and add to total
47 total_bill += subtotal - (discount_percentage * subtotal) / 100
48
49 return total_bill
50
51
52# Your Cashier object will be instantiated and called as such:
53# obj = Cashier(n, discount, products, prices)
54# param_1 = obj.getBill(product, amount)
55
1class Cashier {
2 // Counter to track the number of customers
3 private int customerCount;
4 // Every nth customer gets a discount
5 private int n;
6 // Discount percentage to apply
7 private int discountPercentage;
8 // Map to store product ID to price mapping
9 private Map<Integer, Integer> productPriceMap = new HashMap<>();
10
11 /**
12 * Constructor to initialize the cashier system
13 * @param n - Every nth customer receives a discount
14 * @param discount - The discount percentage to apply
15 * @param products - Array of product IDs
16 * @param prices - Array of corresponding product prices
17 */
18 public Cashier(int n, int discount, int[] products, int[] prices) {
19 this.n = n;
20 this.discountPercentage = discount;
21
22 // Build the product-price mapping
23 for (int i = 0; i < products.length; i++) {
24 productPriceMap.put(products[i], prices[i]);
25 }
26 }
27
28 /**
29 * Calculate the bill for a customer's purchase
30 * @param product - Array of product IDs being purchased
31 * @param amount - Array of quantities for each product
32 * @return The total bill amount after applying discount if applicable
33 */
34 public double getBill(int[] product, int[] amount) {
35 // Increment customer count and check if this customer gets a discount
36 customerCount++;
37 int applicableDiscount = (customerCount % n == 0) ? discountPercentage : 0;
38
39 double totalBill = 0.0;
40
41 // Calculate the total cost for each product
42 for (int i = 0; i < product.length; i++) {
43 int productId = product[i];
44 int quantity = amount[i];
45
46 // Calculate subtotal for this product
47 int subtotal = productPriceMap.get(productId) * quantity;
48
49 // Apply discount if applicable and add to total bill
50 totalBill += subtotal - (applicableDiscount * subtotal) / 100.0;
51 }
52
53 return totalBill;
54 }
55}
56
57/**
58 * Your Cashier object will be instantiated and called as such:
59 * Cashier obj = new Cashier(n, discount, products, prices);
60 * double param_1 = obj.getBill(product,amount);
61 */
62
1class Cashier {
2public:
3 /**
4 * Constructor to initialize the cashier system
5 * @param n: Every nth customer gets a discount
6 * @param discount: Discount percentage for every nth customer
7 * @param products: Vector of product IDs
8 * @param prices: Vector of product prices corresponding to product IDs
9 */
10 Cashier(int n, int discount, vector<int>& products, vector<int>& prices) {
11 this->nthCustomer = n;
12 this->discountPercent = discount;
13
14 // Build product ID to price mapping
15 for (int i = 0; i < products.size(); ++i) {
16 productPriceMap[products[i]] = prices[i];
17 }
18 }
19
20 /**
21 * Calculate the bill for current customer
22 * @param product: Vector of product IDs being purchased
23 * @param amount: Vector of quantities for each product
24 * @return: Total bill amount after applying discount (if applicable)
25 */
26 double getBill(vector<int> product, vector<int> amount) {
27 // Increment customer counter and check if current customer gets discount
28 customerCount++;
29 int currentDiscount = (customerCount % nthCustomer == 0) ? discountPercent : 0;
30
31 double totalBill = 0.0;
32
33 // Calculate total bill for all products
34 for (int i = 0; i < product.size(); ++i) {
35 // Calculate cost for current product (price * quantity)
36 int productCost = productPriceMap[product[i]] * amount[i];
37
38 // Apply discount if applicable and add to total
39 totalBill += productCost - (currentDiscount * productCost) / 100.0;
40 }
41
42 return totalBill;
43 }
44
45private:
46 int customerCount = 0; // Counter for number of customers served
47 int nthCustomer; // Every nth customer gets discount
48 int discountPercent; // Discount percentage to apply
49 unordered_map<int, int> productPriceMap; // Map from product ID to price
50};
51
52/**
53 * Your Cashier object will be instantiated and called as such:
54 * Cashier* obj = new Cashier(n, discount, products, prices);
55 * double param_1 = obj->getBill(product,amount);
56 */
57
1// Counter for number of customers served
2let customerCount: number = 0;
3// Every nth customer gets discount
4let nthCustomer: number;
5// Discount percentage to apply
6let discountPercent: number;
7// Map from product ID to price
8let productPriceMap: Map<number, number> = new Map();
9
10/**
11 * Initialize the cashier system
12 * @param n - Every nth customer gets a discount
13 * @param discount - Discount percentage for every nth customer
14 * @param products - Array of product IDs
15 * @param prices - Array of product prices corresponding to product IDs
16 */
17function Cashier(n: number, discount: number, products: number[], prices: number[]): void {
18 // Reset customer count for new cashier instance
19 customerCount = 0;
20 nthCustomer = n;
21 discountPercent = discount;
22
23 // Clear and rebuild product ID to price mapping
24 productPriceMap.clear();
25 for (let i = 0; i < products.length; i++) {
26 productPriceMap.set(products[i], prices[i]);
27 }
28}
29
30/**
31 * Calculate the bill for current customer
32 * @param product - Array of product IDs being purchased
33 * @param amount - Array of quantities for each product
34 * @returns Total bill amount after applying discount (if applicable)
35 */
36function getBill(product: number[], amount: number[]): number {
37 // Increment customer counter and check if current customer gets discount
38 customerCount++;
39 const currentDiscount: number = (customerCount % nthCustomer === 0) ? discountPercent : 0;
40
41 let totalBill: number = 0.0;
42
43 // Calculate total bill for all products
44 for (let i = 0; i < product.length; i++) {
45 // Calculate cost for current product (price * quantity)
46 const productPrice: number = productPriceMap.get(product[i]) || 0;
47 const productCost: number = productPrice * amount[i];
48
49 // Apply discount if applicable and add to total
50 totalBill += productCost - (currentDiscount * productCost) / 100.0;
51 }
52
53 return totalBill;
54}
55
Time and Space Complexity
Time Complexity:
-
Initialization (
__init__
):O(n)
, wheren
is the number of products. This is because creating the dictionaryd
requires iterating through theproducts
andprices
lists once usingzip()
, which takes linear time proportional to the number of products. -
getBill
method:O(m)
, wherem
is the number of products being purchased (length of theproduct
list in the method call). The method iterates through theproduct
andamount
lists once usingzip()
, and for each item, it performs constant time operations (dictionary lookup, multiplication, and arithmetic operations).
Space Complexity:
O(n)
, wheren
is the number of products. The space is primarily consumed by the dictionaryd
that stores the mapping of all products to their prices. The dictionary containsn
key-value pairs. Other variables (i
,n
,discount
) use constant spaceO(1)
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Integer Division vs Float Division
A common mistake is using integer division when calculating the discount, which can lead to incorrect results in languages that distinguish between integer and float division.
Pitfall Example:
# Wrong - in Python 2 or some other languages, this could cause integer division total_bill += subtotal - (discount_percentage * subtotal) // 100 # Using // instead of /
Solution: Always use float division and ensure the result is a float:
# Correct total_bill += subtotal - (discount_percentage * subtotal) / 100 # Or explicitly convert to float total_bill += subtotal * (100 - discount_percentage) / 100.0
2. Customer Counter Reset
Some implementations might accidentally reset the customer counter or fail to properly maintain state between calls.
Pitfall Example:
def getBill(self, product: List[int], amount: List[int]) -> float:
customer_count = 0 # Wrong - creates local variable instead of using instance variable
customer_count += 1
# This will always treat every customer as the first customer
Solution:
Always use the instance variable with self
:
def getBill(self, product: List[int], amount: List[int]) -> float:
self.customer_count += 1 # Correct - modifies instance variable
3. Incorrect Discount Formula Application
Applying the discount formula incorrectly, especially when mixing the percentage calculation.
Pitfall Example:
# Wrong - applies discount to each item individually before summing
for product_id, quantity in zip(product, amount):
price = self.product_price_map[product_id]
if self.customer_count % self.n == 0:
price = price * (100 - self.discount) / 100 # Modifying price directly
total_bill += price * quantity
Solution: Calculate the subtotal first, then apply discount to the total:
# Better approach - clearer logic
subtotal = sum(self.product_price_map[pid] * qty for pid, qty in zip(product, amount))
if self.customer_count % self.n == 0:
return subtotal * (100 - self.discount) / 100
return subtotal
4. Missing Product ID in Hash Table
Not handling the case where a product ID might not exist in the hash table (though the problem assumes all product IDs are valid).
Pitfall Example:
# Could raise KeyError if product_id not in dictionary subtotal = self.product_price_map[product_id] * quantity
Solution: Add defensive programming if needed:
# Defensive approach
subtotal = self.product_price_map.get(product_id, 0) * quantity
# Or with error handling
if product_id not in self.product_price_map:
raise ValueError(f"Product {product_id} not found")
5. Precision Issues with Float Arithmetic
When dealing with money calculations, floating-point arithmetic can introduce precision errors.
Pitfall Example:
# Multiple float operations can accumulate rounding errors
for product_id, quantity in zip(product, amount):
subtotal = self.product_price_map[product_id] * quantity
discounted = subtotal - (discount_percentage * subtotal) / 100
total_bill += discounted # Accumulating potential rounding errors
Solution: Use the Decimal module for precise monetary calculations if needed:
from decimal import Decimal, ROUND_HALF_UP
# For precise monetary calculations
subtotal = Decimal(str(price)) * Decimal(str(quantity))
discount_amount = (subtotal * Decimal(str(discount_percentage)) / Decimal('100'))
final_amount = subtotal - discount_amount
# Round to 2 decimal places for currency
final_amount = final_amount.quantize(Decimal('0.01'), rounding=ROUND_HALF_UP)
However, since the problem accepts answers within 10^-5
of the actual value, regular float arithmetic is sufficient for this specific case.
What data structure does Breadth-first search typically uses to store intermediate states?
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!