Whiteboard Implementation Practice - AXON

Here is solutions for practice whiteboard coding in AXON.

1. User Key-Value Timestamp

class Location:
    def __init__(self, lat, long, timestamp):
        self.lat = lat
        self.long = long
        self.timestamp = timestamp

    def __str__(self):
        return f"({self.lat}{self.long}{self.timestamp})"

class LocationStore:
    def __init__(self):
        # Use a hashmap: user_id -> [Location]
        self.store = {}

    def get(self, user_id, query_time):
        if user_id not in self.store:
            return None

        # Get all the locations of user
        locations = self.store[user_id]
        n = len(locations)

        # Find the nearest timestamp
        left = 0
        right = n - 1

        while left <= right:
            mid = left + (right - left) // 2

            # Current location
            loc = locations[mid]

            if loc.timestamp < query_time:
                left = mid + 1
            else:
                right = mid - 1

        return locations[left]

    def set(self, user_id, location):
        if user_id not in self.store:
            self.store[user_id] = []

        self.store[user_id].append(location)


def main():
    locationStore = LocationStore()

    # user_id = 1, (1, 3, 1), (2, 5, 3), (3, 5, 5)
    location_1 = Location(1,3,1)
    location_2 = Location(2,5,3)
    location_3 = Location(3,5,5)

    # Set location store
    locationStore.set(1, location_1)
    locationStore.set(1, location_2)
    locationStore.set(1, location_3)

    # Get location
    result = locationStore.get(1, 5)
    print(result)


main()


2. Encrypt, Descrypt, Hash Code

class FuzzyClass:
    ALPHABET_SIZE = 26
    BASE_CHR = ord('a')

    def __init__(self, secret):
        self.secret = secret
        self.block_size = len(secret)

    def _idx2char(self, idx):
        # 1: 'a', 2: 'b', 3: 'c'
        return (idx - 1 + BASE_CHR)

    def _char2idx(self, chr):
        # 'a': 1, 'b': 2,...
        return ord(chr) - BASE_CHR

    def _addChar(self, chr1, chr2):
        v1 = self._char2idx(chr1)
        v2 = self._char2idx(chr2)

        # total
        total = (v1 + v2) % ALPHABET_SIZE
        return self._idx2char(total)

    def _subChar(self, chr1, chr2):
        v1 = self._char2idx(chr1)
        v2 = self._char2idx(chr2)

        # total
        sub = (v1 - v2) % ALPHABET_SIZE

        # Have handle case v1 - v2 negative
        return self._idx2char(sub)

    def _addString(self, str1, str2):
        result = []

        for c1, c2 in zip(str1, str2):
            addChar = self._addChar(c1, c2)
            result.append(addChar)

        return ''.join(result)

    def encrypt(self, data):
        result = []

        for i, chr in enumerate(data):
            secret_chr = self.secret[i % ALPHABET_SIZE]
            result.append(self._addChar(chr, secret_chr))

        return "".join(result)


    def descrypt(self, data):
        result = []

        for i, chr in enumerate(data):
            secret_chr = self.secret[i % ALPHABET_SIZE]
            result.append(self._subChar(chr, secret_chr))

        return "".join(result)


    def hash(self, data):
        # Hash the secret based on data
        N = len(data)

        # Add temp value
        temp = self.secret

        for i in range(0, N, self.block_size):
            block_str = self.secret[i, i + block_size]
            temp = self._addString(temp, block_str)

        return temp

3. Insert/Delete/Random O(1)

import random
from collections import defaultdict

class InsertDeleteRandomO(1):
    def __init__(self):
        self.arr = []

        # Use for quick query
        self.pos = defaultdict(set) # id: hashset of indexes, 4: {1,2,3}

    def insert(self, id: int) -> bool:
        existed = id in self.pos and len(self.pos[id]) > 0

        # Add to arr and pos
        self.arr.append(id)
        self.pos[id].add(len(self.arr) - 1)

        return existed


    def delete(self, id: int) -> bool:
        if id not in self.pos or len(self.pos[id]) == 0:
            return False

        # Get remove_idx and assign it with last_val, pop last_val
        remove_idx = self.pos[id].pop()
        last_val = self.arr[-1]

        # Add to arr
        self.arr[remove_idx] = last_val
        self.dict[last_val].add(remove_idx)

        # Remove last_val
        self.dict[last_val].discard(len(self.arr) - 1)
        self.arr.pop()

        return True

    def random(self) -> int:
        return random.choice(self.arr)

4. Police Parking Garage

from collections import deque
from typing import List

class PoliceGarage:
    def __init__(self, carIds: List[str]):
        # Stack of availables car
        self.stack = deque(carIds)

        # Queue of check-out cars
        self.check_out = deque()

    # Move to stack to checkout
    def checkoutCar(self) -> str:
        if not self.stack:
            raise Exception("No car can checkout")

        car = self.stack.pop()
        self.checkout.append(car)

        return car

    # Move from checkout to stack
    def checkInCar(self) -> str:
        if not self.checkout:
            raise Exception("No car to check-in")

        car = self.check_out.popLeft()
        self.stack.append(car)

        return car

    def getNextCars(self, n: int) -> List[str]:
        temp_stack = deque(self.stack)
        temp_queue = deque(self.queue)

        # All the queue checkin
        while temp_queue:
            temp_stack.append(temp_queue.popLeft())

        # Filter n car in stack
        result = []
        for i in range(min(n, len(temp_stack))):
            result.append(temp_stack.pop())

        return result

5. Inorder Shuffle

class InOrderSuffle:
    # Naive Solution
    def isInterLeave(self, s1: str, s2: str, s3: str) -> bool:
        n = len(s1)
        m = len(s2)
        k = len(s3)

        if k != n + m:
            return False

        def dfs(i, j):
            # Base case
            if i == n and j == m:
                return True

            k = i + j

            # Backtrack
            if i < len(s1) and s1[i] == s3[k]:
                if dfs(i + 1, j):
                    return True

            if j < len(s2) and s2[j] == s3[k]:
                if dfs(i, j + 1):
                    return True

            return False

        return dfs(0, 0)

    # Top-down solution
    def isInterLeave(self, s1: str, s2: str, s3: str) -> bool:
        n = len(s1)
        m = len(s2)
        k = len(s3)

        if k != n + m:
            return False

        dp = {}

        def dfs(i, j):
            # Step 1: Base case
            if i == n and j == m:
                return True

            k = i + j

            if (i, j) in dp:
                return dp[(i, j)]

            # Step 2: Backtrack
            res = False

            if i < len(s1) and s1[i] == s3[k]:
                res = dfs(i + 1, j)

            if j < len(s2) and s2[j] == s3[k]:
                res = dfs(i, j + 1)

            dp[(i, j)] = res

            return res

        return dfs(0, 0)


    # Bottom-up solution
    def isInterLeave(self, s1: str, s2: str, s3: str) -> bool:
        m, n, k = len(s1), len(s2), len(s3)

        if m + n != k:
            return False

        # Step 1: Base case
        dp = [[False] * (n + 1) for _ in range(m + 1)]
        dp[0][0] = True

        for j in range(1, n + 1):
            dp[0][j] = dp[0][j - 1] and s2[j - 1] = s3[j - 1]

        for i in range(1, m + 1):
            dp[i][0] = dp[i - 1][0] and s1[i - 1] = s3[i - 1]

        # Step 2: Update
        for i in range(m + 1):
            for j in range(n + 1):
                dp[i][j] = (dp[i - 1][j] and s1[i - 1] == s3[i + j - 1])
                        or (dp[i][j - 1] and s2[j - 1] == s3[i + j - 1])

        return dp[m][n]

6. K Closest Points to Origin

import heapq

class Solution:
    def _distance(self, x, y):
        return x ** 2 + y ** 2

    def kClosest(self, points: List[List[int]], k: int) -> List[List[int]]:
        maxHeap = []

        for x, y in points:
            # heap push
            dist = -self._distance(x, y)
            heapq.heappush(maxHeap, [dist, x, y])

            if len(maxHeap) > k:
                heapq.heappop(maxHeap)

        res = []
        while maxHeap:
            dist, x, y = heapq.heappop(maxHeap)
            res.append([x, y])

        return res


7. Number of Islands

class Solution:

    # DFS
    def numIslands(self, grid: List[List[str]]) -> int:
        directions = [[-1, 0], [1, 0], [0, -1], [0, 1]]
        ROWS, COLS = len(grid), len(grid[0])

        def dfs(r, c):
            # Base case
            if r < 0 or r >= ROWS or c < 0 or c >= COLS or grid[r][c] == "0":
                return

            # Backtrack or add visited
            grid[r][c] = "0"
            for dr, dc in directions:
                dfs(r + dr, c + dc)

        count = 0
        for r in range(ROWS):
            for c in range(COLS):
                if grid[r][c] == "1":
                    dfs(r, c)
                    count += 1

        return count
from collections import deque

class Solution:

    # DFS
    def numIslands(self, grid: List[List[str]]) -> int:
        directions = [[-1, 0], [1, 0], [0, -1], [0, 1]]
        ROWS, COLS = len(grid), len(grid[0])

        def bfs(r, c):
            q = deque()
            grid[r][c] = "0"
            q.append([r, c])

            while q:
                row, col = q.popleft()

                for dr, dc in directions:
                    nr, nc = row + dr, col + dc

                    # Base case
                    if nr < 0 or nr >= ROWS or nc < 0 or nc >= COLS or grid[nr][nc] == "0":
                        continue

                    # Backtrack
                    q.append([nr, nc])
                    grid[nr][nc] = "0"

        count = 0
        for r in range(ROWS):
            for c in range(COLS):
                if grid[r][c] == "1":
                    bfs(r, c)
                    count += 1

        return count


8. Valid Palindrome

class Solution:
    def alphaNum(self, c):
        return (ord('A') <= ord(c) <= ord('Z') or
                ord('a') <= ord(c) <= ord('z') or
                ord('0') <= ord(c) <= ord('9'))

    def isPalindrome(self, s: str) -> bool:
        left, right = 0, len(s) - 1
        while left <= right:
            while left < right and not self.alphaNum(s[left]):
                left += 1

            while left < right and not self.alphaNum(s[right]):
                right -= 1

            if s[left].lower() != s[right].lower():
                return False

            left += 1
            right -= 1

        return True

9. N-Queens

class Solution:
    def solveNQueens(self, n: int) -> List[List[str]]:
        col = set()
        posDiag = set()
        negDiag = set()

        res = []
        board = [["."] * n for i in range(n)]

        def backtrack(r):
            # Base case
            if r == n:
                copy = ["".join(row) for row in board]
                res.append(copy)
                return

            for c in range(n):
                if c in col or (r + c) in posDiag or (r - c) in negDiag:
                    continue

                # Current
                col.add(c)
                posDiag.add(r + c)
                negDiag.add(r - c)
                board[r][c] = "Q"

                # Backtrack another row
                backtrack(r + 1)

                col.remove(c)
                posDiag.remove(r + c)
                negDiag.remove(r - c)
                board[r][c] = "."

        backtrack(0)
        return res

10. Roman to Integer

class Solution:
    def romanToInt(self, s: str) -> int:
        roman = {
            "I": 1,
            "V": 5,
            "X": 10,
            "L": 50,
            "C": 100,
            "D": 500,
            "M": 1000
        }

        n = len(s)
        res = 0
        for i in range(n):
            if i + 1 < len(s) and roman[s[i]] < roman[s[i + 1]]:
                res -= roman[s[i]]
            else:
                res += roman[s[i]]

        return res
class Solution:
    def intToRoman(self, num: int) -> str:
        roman = {
            "M": 1000,
            "CM": 900,
            "D": 500,
            "CD": 400,
            "C": 100,
            "XC": 90,
            "L": 50,
            "XL": 40,
            "X": 10,
            "IX": 9,
            "V": 5,
            "IV": 4,
            "I": 1
        }

        res = ""
        for key, value in roman.items():
            count = num // value

            if count:
                res += count * key
                num -= count * value

        return res

11. Edit Distance

class Solution:
    def minDistance(self, word1: str, word2: str) -> int:
        m, n = len(word1), len(word2)

        def dfs(i, j):

            # Base case
            if i == m:
                return n - j

            if j == n:
                return m - i

            if word1[i] == word2[j]:
                return dfs(i + 1, j + 1)
            else:
                # Backtrack
                res = min(dfs(i + 1, j), dfs(i, j + 1), dfs(i + 1, j + 1))
                return res + 1

        return dfs(0, 0)
class Solution:
    def minDistance(self, word1: str, word2: str) -> int:
        m, n = len(word1), len(word2)
        dp = {}

        def dfs(i, j):
            # Base case
            if i == m:
                return n - j

            if j == n:
                return m - i

            if (i, j) in dp:
                return dp[(i, j)]

            if word1[i] == word2[j]:
                dp[(i, j)] = dfs(i + 1, j + 1)
            else:
                # Backtrack
                res = min(dfs(i + 1, j), dfs(i, j + 1), dfs(i + 1, j + 1))
                dp[(i, j)] = res + 1

            return dp[(i, j)]

        return dfs(0, 0)

class Solution:
    def minDistance(self, word1: str, word2: str) -> int:
        # Base case
        n, m = len(word1), len(word2)
        dp = [[0] * (m + 1) for _ in range(n + 1)]

        for i in range(n + 1):
            # Convert word[:i] to empty string
            dp[i][0] = i
        for j in range(m + 1):
            # Convert empty string to word[:j]
            dp[0][j] = j

        # Update
        for i in range(1, n + 1):
            for j in range(1, m + 1):
                if word1[i - 1] == word2[j - 1]:
                    dp[i][j] = dp[i - 1][j - 1]
                else:
                    dp[i][j] = 1 + min(dp[i][j - 1], dp[i - 1][j], dp[i - 1][j - 1])

        return dp[n][m]

12. Longest Substring Without Repeating Characters

  • Keep l = 0, increase right => If reach the condition, shrink the left, left += 1.
class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        charSet = set()
        l = 0
        res = 0

        for r in range(len(s)):
            while s[r] in charSet:
                charSet.remove(s[l])
                l += 1

            charSet.add(s[r])
            res = max(res, r - l + 1)

        return res
December 14, 2025