Chapter 21

Math & Number Theory

Master mathematical foundations essential for algorithmic problem-solving. Learn prime number algorithms, modular arithmetic, combinatorics, bit manipulation, and matrix exponentiation with complete C# implementations and 15+ interview-ready solutions.

12
Core Topics
40+
C# Examples
15+
Practice Problems
20
Interview Tips
Table of Contents
21.1

Basic Math for Coding

Foundation concepts that appear in virtually every mathematical problem: integer overflow, modular arithmetic, and the C# Math class utilities.

Integer Overflow & Range

C# integer types have fixed ranges. Understanding overflow is critical:

TypeMin ValueMax ValueSize
byte02558 bits
sbyte-1281278 bits
int-2,147,483,6482,147,483,64732 bits
long-9,223,372,036,854,775,8089,223,372,036,854,775,80764 bits
Overflow Detection & Math.Max/Min
Safe operations using C# Math class and overflow checking.
Integers Overflow
1
Time
O(1)
Space
O(1)
C#
// Math class utilities
public static void MathUtilities()
{
    // Max and Min
    int max = Math.Max(10, 20); // 20
    int min = Math.Min(10, 20); // 10

    // Absolute value
    int abs = Math.Abs(-42); // 42

    // Power and square root
    double power = Math.Pow(2, 10); // 1024
    double sqrt = Math.Sqrt(16); // 4

    // Rounding
    int ceil = (int)Math.Ceiling(3.2); // 4
    int floor = (int)Math.Floor(3.9); // 3
}

// Overflow detection
public static bool WillOverflow(int a, int b)
{
    // Check: a + b > int.MaxValue
    return b > int.MaxValue - a;
}

// Safe addition with long
public static long SafeAdd(long a, long b)
{
    if (b > long.MaxValue - a)
        throw new OverflowException();
    return a + b;
}

Key Points: Use Math.Max/Min for comparisons, Math.Abs for magnitude, Math.Pow/Sqrt for exponents and roots, Math.Ceiling/Floor for rounding. When adding/multiplying large numbers, use long or check bounds beforehand.

Modular Arithmetic Foundation

Modular arithmetic reduces large numbers and prevents overflow. The operation a % m gives remainder when dividing a by m.

šŸ’”
Why Modular Arithmetic? Prevents overflow in large calculations, essential for cryptography, and simplifies complex number patterns. Problems often ask "find answer modulo 10^9+7".
21.2

GCD & LCM: Greatest Common Divisor & Least Common Multiple

Fundamental number theory operations with elegant recursive and iterative solutions. GCD via Euclidean algorithm is O(log n) and appears frequently in interview problems.

Euclidean Algorithm (Iterative)

The most efficient GCD method. Based on: GCD(a, b) = GCD(b, a mod b), continuing until b = 0.

GCD - Euclidean Algorithm (Iterative)
Find greatest common divisor of two numbers in O(log n) time.
Number Theory Classic
2
Time
O(log n)
Space
O(1)
C#
public static int GCD(int a, int b)
{
    // Base case: when b is 0, GCD is a
    while (b != 0)
    {
        int temp = b;
        b = a % b;
        a = temp;
    }
    return a;
}

// Example usage
int result = GCD(48, 18);
// Step 1: GCD(48, 18) -> a=48, b=18
// Step 2: a=18, b=48%18=12 -> GCD(18, 12)
// Step 3: a=12, b=18%12=6 -> GCD(12, 6)
// Step 4: a=6, b=12%6=0 -> GCD(6, 0)
// Return 6
1
While b ≠ 0, perform modulo operation and swap
2
Each iteration: a mod b becomes smaller
3
When b = 0, a contains the GCD

GCD - Recursive

Elegant recursive version of same algorithm:

C#
public static int GCDRecursive(int a, int b)
{
    if (b == 0) return a;
    return GCDRecursive(b, a % b);
}

LCM via GCD

Least Common Multiple uses the identity: LCM(a, b) = (a Ɨ b) / GCD(a, b)

LCM - Least Common Multiple
Find smallest multiple common to both numbers using GCD.
Number Theory Formula
3
C#
public static long LCM(long a, long b)
{
    return (a / GCD((int)a, (int)b)) * b;
}

// Example: LCM(12, 18)
// GCD(12, 18) = 6
// LCM = (12 / 6) * 18 = 2 * 18 = 36

Note: Use long to prevent overflow when multiplying a Ɨ b.

Extended GCD

Finds integers x, y such that ax + by = GCD(a, b). Used in modular inverse calculations.

C#
public static (int gcd, int x, int y) ExtendedGCD(int a, int b)
{
    if (b == 0) return (a, 1, 0);

    var (gcd, x1, y1) = ExtendedGCD(b, a % b);
    int x = y1;
    int y = x1 - (a / b) * y1;

    return (gcd, x, y);
}
21.3

Prime Numbers

Critical algorithms for identifying and generating primes. Trial division is O(√n), Sieve of Eratosthenes is O(n log log n) for range operations.

Trial Division - Check if Prime

Basic primality test by checking divisibility up to √n. Optimization: only check odd numbers after 2.

Trial Division - Is Prime?
Check if a number is prime in O(√n) time.
Primes Single Check
4
Time
O(√n)
Space
O(1)
C#
public static bool IsPrime(int n)
{
    if (n < 2) return false;
    if (n == 2) return true;
    if (n % 2 == 0) return false;

    // Check odd divisors up to sqrt(n)
    for (int i = 3; i * i <= n; i += 2)
    {
        if (n % i == 0) return false;
    }
    return true;
}
šŸ’”
Optimization: Check divisibility only up to √n. If n has a divisor > √n, it must also have one < √n.

Sieve of Eratosthenes

Efficiently find all primes up to n. Mark multiples of each prime as composite. O(n log log n) - much faster than checking each number individually.

Sieve of Eratosthenes
Find all primes up to n in O(n log log n) time.
Primes Range
5
Time
O(n log log n)
Space
O(n)
C#
public static bool[] SieveOfEratosthenes(int n)
{
    bool[] isPrime = new bool[n + 1];
    for (int i = 2; i <= n; i++)
        isPrime[i] = true;

    // Sieve: mark multiples of each prime as composite
    for (int i = 2; i * i <= n; i++)
    {
        if (isPrime[i])
        {
            // Mark all multiples of i as not prime
            for (int j = i * i; j <= n; j += i)
                isPrime[j] = false;
        }
    }
    return isPrime;
}

// Example: find all primes up to 30
bool[] primes = SieveOfEratosthenes(30);
// Result: 2, 3, 5, 7, 11, 13, 17, 19, 23, 29
1
Initialize all numbers 2..n as prime
2
For each prime p, mark all multiples pƗp..n as composite
3
Only check up to √n (inner loop from i²)

Segmented Sieve (Concept)

For very large ranges [L, R] where R - L is small but R is huge, sieve only the range segment. Find small primes up to √R first, then eliminate their multiples in [L, R].

ℹ
When to use: Sieve for finding all primes in a range. Trial division for checking individual numbers. For very large ranges, segmented sieve saves memory.
21.4

Modular Arithmetic

Operations with remainders. Crucial for preventing overflow and solving cyclic/periodic problems. Master (a+b)%m, (aƗb)%m, modular exponentiation, and modular inverse.

Basic Modular Operations

Properties of modulo:

PropertyFormulaExample (mod 7)
Addition(a + b) % m = ((a % m) + (b % m)) % m(5 + 8) % 7 = (5 + 1) % 7 = 6
Multiplication(a Ɨ b) % m = ((a % m) Ɨ (b % m)) % m(6 Ɨ 5) % 7 = (6 Ɨ 5) % 7 = 2
Subtraction(a - b) % m = ((a % m) - (b % m) + m) % m(3 - 8) % 7 = (-5 + 7) % 7 = 2
Powera^b % m = (a % m)^b % m2^10 % 7 = 4

Modular Exponentiation (Fast Power)

Compute a^b mod m efficiently in O(log b) using binary exponentiation. Prevents overflow and computation time explosion.

Modular Exponentiation
Compute a^b mod m in O(log b) time without overflow.
Modular Arithmetic Exponentiation
6
Time
O(log b)
Space
O(1)
C#
public static long ModPow(long a, long b, long m)
{
    long result = 1;
    a %= m;

    while (b > 0)
    {
        // If b is odd, multiply result by a
        if ((b & 1) == 1)
            result = (result * a) % m;

        // Square a and halve b
        a = (a * a) % m;
        b >>= 1;
    }
    return result;
}

// Example: 2^10 mod 1000000007
long ans = ModPow(2, 10, 1000000007); // 1024

Key Insight: Binary exponentiation splits b into powers of 2. If b = 1010ā‚‚ = 8+2, then a^b = a^8 Ɨ a^2. Only O(log b) multiplications needed.

Modular Inverse

Find x such that (a Ɨ x) ≔ 1 (mod m). Exists only when gcd(a, m) = 1. Use Fermat's Little Theorem or Extended GCD.

C#
// Method 1: Using Fermat's Little Theorem
// If m is prime: a^(-1) ≔ a^(m-2) (mod m)
public static long ModInverse(long a, long m)
{
    return ModPow(a, m - 2, m);
}

// Method 2: Using Extended GCD (works for any coprime a, m)
public static long ModInverseGCD(long a, long m)
{
    var (gcd, x, y) = ExtendedGCD((int)a, (int)m);
    if (gcd != 1) return -1; // No inverse exists
    return (x % m + m) % m;
}
21.5

Combinatorics

Count arrangements and selections. Factorials, binomial coefficients (nCr), Pascal's Triangle. Critical for permutation/combination problems.

Factorials & nCr (Binomial Coefficient)

nCr = n! / (r! Ɨ (n-r)!) = number of ways to choose r items from n items.

Binomial Coefficient (nCr)
Calculate combinations efficiently with precomputed factorials.
Combinatorics nCr
7
Time
O(n) precomp, O(1) query
Space
O(n)
C#
private const long MOD = 1000000007;
private long[] fact, inv_fact;

public void PrecomputeFactorials(int maxN)
{
    fact = new long[maxN + 1];
    inv_fact = new long[maxN + 1];

    // Compute factorials
    fact[0] = 1;
    for (int i = 1; i <= maxN; i++)
        fact[i] = (fact[i - 1] * i) % MOD;

    // Compute inverse factorials using Fermat's Little Theorem
    inv_fact[maxN] = ModPow(fact[maxN], MOD - 2, MOD);
    for (int i = maxN - 1; i >= 0; i--)
        inv_fact[i] = (inv_fact[i + 1] * (i + 1)) % MOD;
}

public long NCr(int n, int r)
{
    if (r > n || r < 0) return 0;
    return (fact[n] * inv_fact[r] % MOD) * inv_fact[n - r] % MOD;
}

For small queries: Use Pascal's Triangle approach. For large n: Precompute factorials and inverse factorials using Fermat's Little Theorem (when MOD is prime).

Pascal's Triangle

Dynamic approach for nCr when n and r are bounded. nCr(n, r) = nCr(n-1, r-1) + nCr(n-1, r)

C#
public static long[][] PascalsTriangle(int n)
{
    long[][] dp = new long[n + 1][];

    for (int i = 0; i <= n; i++)
    {
        dp[i] = new long[i + 1];
        dp[i][0] = dp[i][i] = 1;

        for (int j = 1; j < i; j++)
            dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j];
    }
    return dp;
}
21.6

Probability Basics

Expected value calculations, reservoir sampling, Fisher-Yates shuffle. Algorithms that rely on statistical properties.

Expected Value

E(X) = Ī£(x_i Ɨ P(x_i)). In many DSA problems, calculate average outcome across all possibilities.

šŸ’”
Linearity of Expectation: E(X + Y) = E(X) + E(Y) even if X and Y are not independent. Powerful for simplifying complex calculations.

Reservoir Sampling

Randomly select k items from a stream of unknown length, each item equally likely. Classic interview problem.

Reservoir Sampling
Pick k random items from a stream in one pass, O(n) time, O(k) space.
Probability Streaming
8
Time
O(n)
Space
O(k)
C#
public int[] ReservoirSample(int[] stream, int k)
{
    int[] reservoir = new int[k];
    int n = stream.Length;

    // Fill reservoir with first k elements
    for (int i = 0; i < k; i++)
        reservoir[i] = stream[i];

    var rand = new Random();
    // For each element from k onwards
    for (int i = k; i < n; i++)
    {
        // Random index 0 to i (inclusive)
        int j = rand.Next(0, i + 1);

        // If j < k, replace element at j with stream[i]
        if (j < k)
            reservoir[j] = stream[i];
    }
    return reservoir;
}

Probability: Each element has k/n probability of being in final sample, regardless of position.

Fisher-Yates Shuffle

Uniformly randomize array elements in O(n) time. Every permutation equally likely.

Fisher-Yates Shuffle
In-place random shuffle of array elements, O(n) time, O(1) space.
Probability Shuffle
9
Time
O(n)
Space
O(1)
C#
public void Shuffle(int[] nums)
{
    var rand = new Random();

    for (int i = nums.Length - 1; i > 0; i--)
    {
        // Pick random j from 0 to i
        int j = rand.Next(0, i + 1);

        // Swap nums[i] and nums[j]
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
}
1
Start from last element
2
Pick random index from 0 to i (inclusive)
3
Swap and move backwards
21.7

Bit Counting & Number Properties

Power checks, Hamming weight, trailing zeros. Low-level number properties crucial for optimization.

Is Power of Two/Three/Four?

Efficient checks for special numbers using bit operations.

C#
// Power of 2: A power of 2 has exactly one bit set
// n & (n-1) == 0 for powers of 2
public static bool IsPowerOfTwo(int n)
{
    return n > 0 && (n & (n - 1)) == 0;
}

// Power of 3: Check if log_3(n) is integer
public static bool IsPowerOfThree(int n)
{
    if (n <= 0) return false;
    while (n % 3 == 0)
        n /= 3;
    return n == 1;
}

// Power of 4: Power of 2 AND (n-1) has only odd bits set
public static bool IsPowerOfFour(int n)
{
    return n > 0 && (n & (n - 1)) == 0 && (n - 1) % 3 == 0;
}

Hamming Weight (Number of Set Bits)

Count how many 1s in binary representation. Use Brian Kernighan's algorithm: n & (n-1) removes rightmost 1 bit.

C#
// Brian Kernighan's Algorithm: O(# of set bits)
public static int HammingWeight(uint n)
{
    int count = 0;
    while (n > 0)
    {
        n &= n - 1; // Remove rightmost 1 bit
        count++;
    }
    return count;
}

// Alternative: Built-in (modern C#)
int weight = System.Numerics.BitOperations.PopCount(42);

Trailing Zeros in Factorial

Count zeros at end of n! (determined by factors of 5). Formula: ⌊n/5āŒ‹ + ⌊n/25āŒ‹ + ⌊n/125āŒ‹ + ...

C#
// Count trailing zeros in n!
public static int TrailingZeros(int n)
{
    int count = 0;
    for (long i = 5; i <= n; i *= 5)
        count += (int)(n / i);
    return count;
}

// Example: 5! = 120 (1 trailing zero), 10! = 3628800 (2 trailing zeros)
21.8

Matrix Exponentiation

Solve recurrence relations in O(log n). Classic: compute nth Fibonacci number in O(log n) instead of O(n).

Matrix Exponentiation Concept

For Fibonacci: F(n) = F(n-1) + F(n-2), express as matrix multiplication and use fast exponentiation.

Fibonacci via Matrix Exponentiation
Calculate nth Fibonacci in O(log n) time.
Matrix Exponent Fibonacci
10
Time
O(log n)
Space
O(1)
C#
// Matrix multiplication for 2x2 matrices
private long[][] Multiply(long[][] A, long[][] B, long mod)
{
    long[][] C = new long[2][];
    C[0] = new long[2];
    C[1] = new long[2];

    for (int i = 0; i < 2; i++)
    {
        for (int j = 0; j < 2; j++)
        {
            for (int k = 0; k < 2; k++)
                C[i][j] = (C[i][j] + A[i][k] * B[k][j]) % mod;
        }
    }
    return C;
}

// Matrix exponentiation
private long[][] MatPow(long[][] matrix, long n, long mod)
{
    long[][] result = new long[][] {
        new long[] { 1, 0 },
        new long[] { 0, 1 }
    };

    while (n > 0)
    {
        if ((n & 1) == 1)
            result = Multiply(result, matrix, mod);
        matrix = Multiply(matrix, matrix, mod);
        n >>= 1;
    }
    return result;
}

// nth Fibonacci: F(n) = matrix^n [0][1]
public static long Fibonacci(long n, long mod)
{
    if (n == 0) return 0;
    if (n == 1) return 1;

    long[][] matrix = new long[][] {
        new long[] { 1, 1 },
        new long[] { 1, 0 }
    };

    long[][] result = MatPow(matrix, n, mod);
    return result[0][1];
}
ℹ
Why it works: [[1,1],[1,0]]^n = [[F(n+1),F(n)],[F(n),F(n-1)]]. Extract F(n) from position [0][1].
21.9

Geometry Basics

Distance, line intersection, convex hull concepts. Practical geometry operations for coding interviews.

Distance Formula

Euclidean distance: sqrt((x2-x1)² + (y2-y1)²). Often work with squared distance to avoid floating point.

C#
// Euclidean distance (with sqrt)
public static double Distance(double x1, double y1, double x2, double y2)
{
    double dx = x2 - x1;
    double dy = y2 - y1;
    return Math.Sqrt(dx * dx + dy * dy);
}

// Squared distance (faster, avoids floating point)
public static long DistanceSquared(int x1, int y1, int x2, int y2)
{
    long dx = x2 - x1;
    long dy = y2 - y1;
    return dx * dx + dy * dy;
}

Cross Product & Line Intersection

Cross product determines orientation (clockwise/counterclockwise). Essential for line segment intersection.

C#
// Cross product of vectors OA and OB
public static long CrossProduct(int ox, int oy,
                                         int ax, int ay,
                                         int bx, int by)
{
    return (long)(ax - ox) * (by - oy) - (long)(ay - oy) * (bx - ox);
}

// Check if point P lies on segment AB
public static bool OnSegment(int ax, int ay, int bx, int by,
                                    int px, int py)
{
    return px >= Math.Min(ax, bx) && px <= Math.Max(ax, bx) &&
           py >= Math.Min(ay, by) && py <= Math.Max(ay, by);
}

// Convex Hull concept: Graham Scan approach
// Not implementing full here due to complexity, but key is
// sorting by polar angle and maintaining monotonic chain
21.10

Practice Problems with Full C# Solutions

15+ interview problems covering all math topics. Each includes complete, production-ready code.

Problem 1: Count Primes (LeetCode 204)
Count number of primes less than n using Sieve of Eratosthenes.
Sieve LeetCode
P1
Time
O(n log log n)
Space
O(n)
C#
public class Solution {
    public int CountPrimes(int n)
    {
        if (n <= 2) return 0;

        bool[] isPrime = new bool[n];
        for (int i = 2; i < n; i++)
            isPrime[i] = true;

        for (int i = 2; i * i < n; i++)
        {
            if (isPrime[i])
            {
                for (int j = i * i; j < n; j += i)
                    isPrime[j] = false;
            }
        }

        int count = 0;
        for (int i = 2; i < n; i++)
            if (isPrime[i]) count++;

        return count;
    }
}
Problem 2: Power of Three (LeetCode 326)
Check if integer is power of 3.
Power Check
P2
C#
public class Solution {
    public bool IsPowerOfThree(int n)
    {
        if (n <= 0) return false;
        while (n % 3 == 0)
            n /= 3;
        return n == 1;
    }
}
Problem 3: Factorial Trailing Zeroes (LeetCode 172)
Count trailing zeros in n! factorial.
Factorial
P3
C#
public class Solution {
    public int TrailingZeroes(int n)
    {
        int count = 0;
        for (long i = 5; i <= n; i *= 5)
            count += (int)(n / i);
        return count;
    }
}
Problem 4: Happy Number (LeetCode 202)
Detect cycle using Floyd's algorithm. Sum of squares of digits repeatedly.
Floyd Cycle
P4
C#
public class Solution {
    private int GetNext(int n)
    {
        int sum = 0;
        while (n > 0)
        {
            int digit = n % 10;
            sum += digit * digit;
            n /= 10;
        }
        return sum;
    }

    public bool IsHappy(int n)
    {
        int slow = n, fast = GetNext(n);

        while (fast != 1 && slow != fast)
        {
            slow = GetNext(slow);
            fast = GetNext(GetNext(fast));
        }

        return fast == 1;
    }
}
Problem 5: Excel Sheet Column Number (LeetCode 171)
Convert column title to column number (A=1, B=2, ..., Z=26, AA=27).
Base Conversion
P5
C#
public class Solution {
    public int TitleToNumber(string columnTitle)
    {
        int result = 0;

        foreach (char c in columnTitle)
        {
            result = result * 26 + (c - 'A' + 1);
        }

        return result;
    }
}
Problem 6: Roman to Integer (LeetCode 13)
Convert Roman numeral string to integer.
Parsing
P6
C#
public class Solution {
    public int RomanToInt(string s)
    {
        var romanValues = new Dictionary<char, int> {
            { 'I', 1 }, { 'V', 5 }, { 'X', 10 },
            { 'L', 50 }, { 'C', 100 }, { 'D', 500 },
            { 'M', 1000 }
        };

        int total = 0;
        for (int i = 0; i < s.Length; i++)
        {
            int val = romanValues[s[i]];
            if (i + 1 < s.Length && val < romanValues[s[i + 1]])
                total -= val;
            else
                total += val;
        }

        return total;
    }
}

// Problem 7: Integer to Roman (LeetCode 12)
Problem 7: Integer to Roman (LeetCode 12)
Convert integer to Roman numeral string.
Conversion
P7
C#
public class Solution {
    public string IntToRoman(int num)
    {
        int[] values = { 1000, 900, 500, 400, 100, 90, 50, 40,
                       10, 9, 5, 4, 1 };
        string[] symbols = { "M", "CM", "D", "CD", "C", "XC",
                            "L", "XL", "X", "IX", "V", "IV", "I" };

        StringBuilder sb = new StringBuilder();

        for (int i = 0; i < values.Length; i++)
        {
            while (num >= values[i])
            {
                sb.Append(symbols[i]);
                num -= values[i];
            }
        }

        return sb.ToString();
    }
}

// Problem 8: Sqrt(x) (LeetCode 69) - Newton's Method
Problem 8: Sqrt(x) (LeetCode 69)
Compute integer square root using Newton's method or binary search.
Newton's Method
P8
C#
public class Solution {
    // Method 1: Newton's Method (faster convergence)
    public int MySqrt(int x)
    {
        if (x == 0) return 0;

        long result = x;
        while (result * result > x)
        {
            result = (result + x / result) / 2;
        }

        return (int)result;
    }

    // Method 2: Binary Search
    public int MySqrtBinary(int x)
    {
        long left = 0, right = x;

        while (left <= right)
        {
            long mid = left + (right - left) / 2;
            long sq = mid * mid;

            if (sq == x) return (int)mid;
            else if (sq < x) left = mid + 1;
            else right = mid - 1;
        }

        return (int)right;
    }
}

// Problem 9: Pow(x, n) (LeetCode 50) - Fast Exponentiation
Problem 9: Pow(x, n) (LeetCode 50)
Compute x^n efficiently in O(log n) using fast exponentiation.
Exponentiation
P9
C#
public class Solution {
    public double MyPow(double x, int n)
    {
        long N = n;
        if (N < 0)
        {
            x = 1 / x;
            N = -N;
        }

        double result = 1;
        double base_val = x;

        while (N > 0)
        {
            if ((N & 1) == 1)
                result *= base_val;

            base_val *= base_val;
            N >>= 1;
        }

        return result;
    }
}
      
Problem 10: FizzBuzz (LeetCode 412)
Classic modulo problem: print "Fizz" for multiples of 3, "Buzz" for multiples of 5.
Modulo
P10
C#
public class Solution {
    public IList<string> FizzBuzz(int n)
    {
        var result = new List<string>();

        for (int i = 1; i <= n; i++)
        {
            bool fizz = (i % 3 == 0);
            bool buzz = (i % 5 == 0);

            if (fizz && buzz)
                result.Add("FizzBuzz");
            else if (fizz)
                result.Add("Fizz");
            else if (buzz)
                result.Add("Buzz");
            else
                result.Add(i.ToString());
        }

        return result;
    }
}
      
Problem 11: Add Binary (LeetCode 67)
Add two binary strings and return result as binary string.
Binary Arithmetic
P11
C#
public class Solution {
    public string AddBinary(string a, string b)
    {
        StringBuilder sb = new StringBuilder();
        int carry = 0;
        int i = a.Length - 1;
        int j = b.Length - 1;

        while (i >= 0 || j >= 0 || carry > 0)
        {
            int sum = carry;
            if (i >= 0) sum += a[i--] - '0';
            if (j >= 0) sum += b[j--] - '0';

            sb.Append(sum % 2);
            carry = sum / 2;
        }

        char[] result = sb.ToString().ToCharArray();
        System.Array.Reverse(result);
        return new string(result);
    }
}
      
Problem 12: Multiply Strings (LeetCode 43)
Multiply two non-negative integer strings without converting to integers.
String Arithmetic
P12
C#
public class Solution {
    public string Multiply(string num1, string num2)
    {
        if (num1 == "0" || num2 == "0") return "0";

        int[] result = new int[num1.Length + num2.Length];

        int i = num1.Length - 1;
        int j = num2.Length - 1;

        for (; i >= 0; i--)
        {
            for (int k = j; k >= 0; k--)
            {
                int mul = (num1[i] - '0') * (num2[k] - '0');
                int p1 = i + k, p2 = i + k + 1;
                int sum = mul + result[p2];

                result[p2] = sum % 10;
                result[p1] += sum / 10;
            }
        }

        StringBuilder sb = new StringBuilder();
        foreach (int digit in result)
            if (!(sb.Length == 0 && digit == 0))
                sb.Append(digit);

        return sb.Length == 0 ? "0" : sb.ToString();
    }
}
      
Problem 13: Random Pick with Weight (LeetCode 528)
Pick index with probability proportional to weight using prefix sum + binary search.
Probability Binary Search
P13
C#
public class Solution {
    private int[] prefixSum;
    private Random rand;

    public Solution(int[] w)
    {
        prefixSum = new int[w.Length];
        rand = new Random();

        prefixSum[0] = w[0];
        for (int i = 1; i < w.Length; i++)
            prefixSum[i] = prefixSum[i - 1] + w[i];
    }

    public int PickIndex()
    {
        int target = rand.Next(1, prefixSum[prefixSum.Length - 1] + 1);
        return BinarySearch(target);
    }

    private int BinarySearch(int target)
    {
        int left = 0, right = prefixSum.Length - 1;

        while (left < right)
        {
            int mid = left + (right - left) / 2;
            if (prefixSum[mid] < target)
                left = mid + 1;
            else
                right = mid;
        }

        return left;
    }
}
      
Problem 14: Shuffle an Array (LeetCode 384)
Shuffle array using Fisher-Yates algorithm with O(n) time.
Fisher-Yates
P14
C#
public class Solution {
    private int[] original;
    private Random rand;

    public Solution(int[] nums)
    {
        original = (int[])nums.Clone();
        rand = new Random();
    }

    public int[] Shuffle()
    {
        int[] result = (int[])original.Clone();

        for (int i = result.Length - 1; i > 0; i--)
        {
            int j = rand.Next(0, i + 1);
            int temp = result[i];
            result[i] = result[j];
            result[j] = temp;
        }

        return result;
    }

    public int[] Reset()
    {
        return (int[])original.Clone();
    }
}
      
Problem 15: Ugly Number II (LeetCode 264)
Find nth ugly number (divisible only by 2, 3, 5) using dynamic programming.
Dynamic Programming
P15
C#
public class Solution {
    public int NthUglyNumber(int n)
    {
        int[] dp = new int[n];
        dp[0] = 1;

        int i2 = 0, i3 = 0, i5 = 0;

        for (int i = 1; i < n; i++)
        {
            int next2 = dp[i2] * 2;
            int next3 = dp[i3] * 3;
            int next5 = dp[i5] * 5;

            int nextUgly = Math.Min(next2, Math.Min(next3, next5));
            dp[i] = nextUgly;

            if (nextUgly == next2) i2++;
            if (nextUgly == next3) i3++;
            if (nextUgly == next5) i5++;
        }

        return dp[n - 1];
    }
}
      
Problem 16: Nth Digit (LeetCode 400)
Find the nth digit in sequence 123456789101112131415... using math.
Math Pattern
P16
C#
public class Solution {
    public int FindNthDigit(int n)
    {
        long digitCount = 1; // 1-digit: 1..9 (9 numbers)
        long startNum = 1;
        long countInRange = 9;

        // Find which range (1-digit, 2-digit, etc.) contains nth digit
        while (n > digitCount * countInRange)
        {
            n -= (int)(digitCount * countInRange);
            digitCount++;
            startNum *= 10;
            countInRange *= 10;
        }

        // Find which number and which digit within that number
        long num = startNum + (n - 1) / digitCount;
        int digitIndex = (int)((n - 1) % digitCount);

        return int.Parse(num.ToString()[digitIndex].ToString());
    }
}
      
21.11

Math Tricks for Interviews

Patterns and shortcuts that accelerate problem-solving in coding interviews.

Common Pattern Recognition

PatternTechniqueExample
Divisibility by power of 2Use bit operations (& with mask)n & (n-1) == 0 for power of 2
Modulo arithmetic safetyTake modulo before multiplying(a % m) * (b % m) % m
Large exponentsAlways use binary exponentiationO(log n) instead of O(n)
Factorial moduloPrecompute factorials + inverse factorialsO(n) setup, O(1) queries
Cycle detectionFloyd's cycle detection (tortoise/hare)Happy Number, Linked List Cycle
Prefix sum tricksCumulative sum for range queriesRandom pick with weight

Overflow Detection & Handling

⚠
Critical: Always consider overflow when multiplying or adding large numbers. Use long, check bounds, or take modulo early.
C#
// Safe multiplication: check before multiplying
public static long SafeMultiply(long a, long b, long limit)
{
    if (a > limit / b) throw new OverflowException();
    return a * b;
}

// Safe addition: check before adding
public static long SafeAdd(long a, long b)
{
    if (a > long.MaxValue - b) throw new OverflowException();
    return a + b;
}
      

Precision & Floating Point

When comparing floating point numbers, use epsilon tolerance instead of exact equality:

C#
public static bool AlmostEqual(double a, double b, double epsilon = 1e-9)
{
    return Math.Abs(a - b) < epsilon;
}
      
21.12

Interview Tips & Patterns

Strategic approaches to identify and solve mathematical problems in live coding interviews.

Tip 1
Recognize the Problem Type
Look for keywords: "divisible", "prime", "digit", "power", "factor", "permutation", "probability". These signal specific mathematical techniques.
Tip 2
Always Think About Overflow
When multiplying/adding large numbers, overflow is a real concern. Use long, modulo, or check bounds. Better safe than wrong.
Tip 3
Leverage Precomputation
For queries on same dataset, precompute results (factorials, primes, prefix sums). Transforms O(n) per query to O(1).
Tip 4
Use Bit Operations When Possible
Bit operations are O(1) and often reveal problem structure. Check power of 2 with n & (n-1), toggle with XOR, etc.
Tip 5
Pattern Matching from Examples
For digit/sequence problems, trace through small examples. Patterns (divisibility, cycles, recurrences) often emerge from manual work.
Tip 6
Know Your Complexity Trade-offs
Sieve O(n log log n) vs trial division O(sqrt n). Precomputation O(n) + O(1) query vs O(n) per query. Choose based on constraints.

Common Gotchas

ā›”
Gotcha 1: Confusing GCD with LCM formulas. LCM = (a Ɨ b) / GCD, not a + b or a Ɨ b.
ā›”
Gotcha 2: Forgetting edge cases (0, 1, negative numbers). Test these explicitly.
ā›”
Gotcha 3: Off-by-one errors in sieve or factorization loops. Double-check boundary conditions.
ā›”
Gotcha 4: Modular inverse only exists when gcd(a, m) = 1. Check this condition first.

When to Use Each Technique

Sieve of Eratosthenes
Use when finding all primes up to n, especially in competitive programming. Space O(n), time O(n log log n). Best for n < 10^7.
Problem: Count Primes (LC 204)
Trial Division
Use for checking single numbers if primality. Time O(sqrt n), space O(1). Good for one-off checks.
Problem: Ugly Number (LC 263)
Binary Exponentiation
Always use for large exponents. O(log n) instead of O(n). Works with modulo to prevent overflow.
Problem: Pow(x, n) (LC 50)
Matrix Exponentiation
Use for linear recurrences like Fibonacci, in O(log n). Overkill for small n, essential for large n.
Problem: Fibonacci
Extended GCD
Use to find modular inverse or solve Bezout's identity. More general than Fermat's Little Theorem.
Problem: Modular Inverse
Floyd's Cycle Detection
Use when cycle detection is implicit (value transformations). O(n) time, O(1) space.
Problem: Happy Number (LC 202)

Template Answers

Prepare these before interviews to save time: