</> MAANG.io
coding interview · 201

Intermediate

Advanced coding interview preparation covering complex algorithms, system design, and optimization techniques for senior-level positions.

0/182 solved 0% complete

Number and Math

What is this?

Three problems that each have an obvious slow solution and a short correct one. Testing every number up to n for primality is quadratic-ish; crossing off multiples is nearly linear. Checking every triple for the largest product is cubic; sorting once makes it two comparisons. Testing whether a number's only prime factors are 2, 3 and 5 by factorising it fully is unnecessary; dividing those factors out and checking what is left is three loops.

The pattern is the same each time: stop asking about every candidate and start eliminating whole classes at once.

Which number shortcut to use

💡 Fun fact: the Sieve of Eratosthenes is around 2,200 years old and still the practical way to list primes below a few million. Two refinements make it fast: start crossing off at p × p — every smaller multiple of p already has a smaller prime factor and was struck out earlier — and stop the outer loop at √n, because any composite below n has a factor no larger than that. Those two details take it from O(n log n)-ish to O(n log log n).

🔓 The 4 problems in this chapter are free. Sign in with Google or Microsoft to start solving.


The one-line idea: eliminate classes rather than testing individuals — divide known factors out, cross off multiples in bulk, or sort so that only two candidate answers remain to compare.


1. Divide the factors out

def is_ugly(n):
    if n <= 0:
        return False
    for p in (2, 3, 5):
        while n % p == 0:
            n //= p
    return n == 1

If dividing away every 2, 3 and 5 leaves exactly 1, the number had no other prime factors. The n <= 0 guard matters: zero would loop forever and negatives are excluded by definition.

2. Sieve, do not test

def count_primes(n):
    if n < 3:
        return 0
    is_prime = [True] * n
    is_prime[0] = is_prime[1] = False
    p = 2
    while p * p < n:
        if is_prime[p]:
            for multiple in range(p * p, n, p):     # start at p*p, not 2p
                is_prime[multiple] = False
        p += 1
    return sum(is_prime)

Starting at p × p is not a micro-optimisation — every multiple k × p with k < p has a prime factor smaller than p and was already crossed off when that smaller prime was processed.

3. Enumerate the sign cases

The largest product of three numbers is one of exactly two candidates:

  • the three largest values, or
  • the two most negative values times the largest.

The second wins when two large negatives multiply into a large positive. Sorting makes both readable in one line:

nums.sort()
return max(nums[-1] * nums[-2] * nums[-3],
           nums[0] * nums[1] * nums[-1])

There is a linear alternative that tracks the three largest and two smallest in one pass — worth mentioning as the O(n) improvement once the sorted version is on the board.


4. A 30-second worked example

nums = [-10, -10, 1, 3, 2]

sorted: [-10, -10, 1, 2, 3]

candidate A — three largest:      1 x 2 x 3       = 6
candidate B — two smallest x max: -10 x -10 x 3   = 300  ✅

answer = 300

Taking only the three largest gives 6 and is wrong. The two negatives cancel, which is exactly the case the second candidate exists to catch.


5. Where you'll actually meet this

  • Cryptography and hashing. Prime generation underpins key selection and table sizing.
  • Numeric libraries. Smooth-number tests (only small prime factors) decide whether a fast Fourier transform size is efficient.
  • Financial and scoring calculations. Extreme-product selections where negative values are meaningful.
  • Load and shard sizing. Choosing sizes with convenient factorisations.
  • Competitive and simulation code. Precomputed prime tables are a standard warm-up step.

6. Problems in this chapter

▶ Ugly Number

Decide whether a number's only prime factors are 2, 3 and 5. Divide each out repeatedly and check that 1 remains.
Pattern: factor elimination. Target: O(log n) time, O(1) space.

▶ Count Primes

Count primes below n. Sieve of Eratosthenes, crossing off from p × p with the outer loop bounded by √n.
Pattern: sieve. Target: O(n log log n) time, O(n) space.

▶ Maximum Product of Three Numbers

Largest product of any three values. Sort, then take the better of the top three and the two smallest times the largest.
Pattern: sign-case enumeration. Target: O(n log n) sorted, or O(n) with a single tracking pass.


7. Common pitfalls 🚫

  • Looping forever on zero in the factor test. Guard n <= 0 first.
  • Starting the sieve at 2p. Correct but slower; p × p is the right lower bound.
  • Running the outer sieve loop to n. It only needs to reach √n.
  • Off-by-one on "below n". Count Primes excludes n itself, so the array has n entries indexed 0..n-1.
  • Taking only the three largest for the product. Two negatives can beat them.
  • Assuming at least one positive value exists. An all-negative array still has a well-defined answer.
  • Overflow. Three values near 10³ are fine, but in fixed-width languages the general case needs a 64-bit type.
  • Day of the Week — the weekday for a given date. Either count days since a known anchor or apply Zeller's congruence; the leap-year rule (divisible by 4, except centuries, unless divisible by 400) is the part that must be exactly right.

8. Key takeaways

  1. Eliminate classes, not individuals. Cross off multiples; divide out factors.
  2. p × p and √n are the two details that make a sieve fast.
  3. Two candidates decide the extreme product, and the negative case is the one that gets missed.
  4. Sort for clarity, then offer the linear version — showing both is stronger than either alone.
  5. Guard the degenerate inputs. Zero, negatives and tiny n are where these fail.
  6. Know the standard complexities. O(n log log n) for a sieve is worth saying precisely.
  7. Why interviewers like it: short problems with well-known optimal solutions, so they reveal quickly whether you know the standard tools or are improvising.

Order: Ugly Number → Count Primes → Maximum Product of Three Numbers → Day of the Week.

Day of the Week

Core idea: A weekday repeats every 7 days, forever. So if you know one anchor — say, January 1, 1971 was a Friday — then the weekday of any other date is just (number of days between them) mod 7, stepped forward from that anchor. The entire problem collapses to one question: how many days lie between the anchor and the target date? Everything hard about this problem — leap years — lives inside that single count.

Problem, rephrased

You're handed a calendar date as three integers: a day, a month, and a year (the year is somewhere between 1971 and 2100). Return the name of the weekday it falls on — one of "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday".

Think of it like a wall calendar with no weekday labels printed on it — just the numbers. Someone points at a square and asks "what day of the week is that?" You can't look it up; you have to count your way there from a date whose weekday you already know.

The trick is that you can't naively assume every year is 365 days. Some years have a leap day (February 29), which nudges every subsequent date forward by one weekday. Get the leap-year rule wrong and your answer drifts by a day for every leap year you miscounted.

Input (day, month, year) Output Why
31, 8, 2019 "Saturday" LeetCode's own sample
18, 7, 1999 "Sunday" LeetCode's own sample
15, 8, 1993 "Sunday" LeetCode's own sample
1, 1, 1971 "Friday" the anchor date itself — zero days elapsed
29, 2, 2000 "Tuesday" a real leap day (2000 is divisible by 400)

Sign in to continue reading

The rest of this lesson is available with a free account. Signing in with Google or Microsoft is free.

Sign in to read the full lesson

Sign in to MAANG.io

Use your Google or Microsoft account — no password to remember.

Continue with Google Continue with Microsoft

Please accept the terms above to continue.