I ran through the IMC SWE OA for a candidate. The volume is small but the difficulty is real, so here's a debrief for anyone prepping. It's 2 coding problems in 120 minutes on HackerRank with your language of choice, medium-to-hard, mainly testing algorithm fundamentals, engineering sense, and efficient implementation. If you want OA assist or OA live support, you can align your pacing with this debrief.
OA Overview and Time Allocation
| Aspect | Details |
|---|---|
| Platform | HackerRank, language of your choice |
| Time | 120 minutes |
| Volume | 2 coding problems |
| Difficulty | Medium-to-hard |
| Focus | Algorithm fundamentals, engineering sense, efficient implementation |
Two problems, 120 minutes. The key is to avoid getting stuck in problem 1 for the first half hour, which leaves no time to read and shore up edge cases on problem 2. A suggested rhythm:
- Read both problems fully first, and judge which has the clearer state.
- Aim to write a version of problem 1 that passes samples and the main tests within 35 to 45 minutes.
- On problem 2, get the correct state first, then handle space compression, pruning, or complexity optimization.
- Reserve the last 10 minutes to check empty input, extreme values, duplicate elements, integer overflow, and initialization.
Q1: Grid Path Counting with Passes
Problem
Given a grid of 0s and 1s, a robot starts at the top-left (0,0), can only move right or down, and aims to reach the bottom-right. 1 means passable normally, 0 means an obstacle, but the robot may use up to k passes, each consuming one to cross an obstacle — including the start and end cells. Count all valid paths, modulo 10^9 + 7.
Approach
Use 3D dynamic programming. Define dp[i][j][t] as the number of paths reaching (i,j) having used t passes:
- When the current cell is
1, transition directly from thedp[*][*][t]states above and to the left. - When the current cell is
0, it consumes one pass, so transition fromdp[*][*][t-1]. - Initialize the start based on whether it's an obstacle: set
dp[0][0][0]ordp[0][0][1].
Finally sum all end-cell states using 0 through k passes. Time complexity O(nmk), space O(nmk), reducible to O(mk) with a rolling array.
Python Implementation
MOD = 10**9 + 7
def count_paths(grid, k):
"""grid: 0/1 matrix; k: pass budget (obstacles, start, and end all consume).
Returns valid right/down path count from top-left to bottom-right % 1e9+7."""
n, m = len(grid), len(grid[0])
# dp[j][t]: paths reaching column j in the current row having used t passes (rolling)
dp = [[0] * (k + 1) for _ in range(m)]
start_cost = 0 if grid[0][0] == 1 else 1
if start_cost <= k:
dp[0][start_cost] = 1
for i in range(n):
for j in range(m):
if i == 0 and j == 0:
continue
cost = 0 if grid[i][j] == 1 else 1 # an obstacle consumes one pass
ndp = [0] * (k + 1)
for t in range(cost, k + 1):
total = 0
if j > 0:
total += dp[j - 1][t - cost] # coming from the left (already this row)
if i > 0:
total += dp[j][t - cost] # coming from above (dp[j] still prev row)
ndp[t] = total % MOD
dp[j] = ndp
return sum(dp[m - 1]) % MOD
Mind the timing of the "left" and "above" references in the rolling array: when processing (i,j), dp[j-1] should already hold this row's value while dp[j] still holds the previous row's. If the timing feels error-prone, use a full 3D dp[i][j][t] in the exam — it's the safest; compress space only after it passes.
Time complexity: O(nmk). Space complexity: O(mk) with a rolling array, O(nmk) for the full version.
Q2: Capacity-Limited Weighted LFU Cache
Problem
Implement a weighted LFU cache limited by total capacity. Each entry has key, value, size, and the sum of all size cannot exceed capacity:
GET key: if the key exists, return its value, increment its access frequency, and update recency; otherwise return-1.PUT key value size: insert or update. New entries start at frequency 1; updating an existing entry only changes value and size, not frequency, but must refresh recency; if a single entry'ssizeexceeds total capacity, ignore the operation.- When space is insufficient, evict the lowest-frequency entry first; on frequency ties, evict the least recently used, until there's room.
Approach
Use a hash map from key to a cache node, plus frequency buckets each ordered by recency, tracking min_freq and the running total size:
- On
GET, remove the node from its frequency bucket and place it at the head of thefreq+1bucket. - On
PUTof an existing entry, adjust total size and refresh its position; on insert, add it at frequency 1. - On overflow, keep removing the least recently used node from the
min_freqbucket, updating the map and total size.
With a hash map plus frequency buckets, each access, update, and eviction runs in amortized O(1). Here Python's OrderedDict per frequency bucket naturally maintains LRU order within the bucket, keeping the code short and equivalent to hand-writing a doubly linked list.
Python Implementation
from collections import defaultdict, OrderedDict
class WeightedLFU:
def __init__(self, capacity):
self.capacity = capacity
self.size_used = 0
self.node = {} # key -> [value, size, freq]
self.freq = defaultdict(OrderedDict) # freq -> OrderedDict[key]; head = LRU
self.min_freq = 0
def _remove_from_bucket(self, key, f):
"""Pull key out of frequency bucket f; if empty, clear it and raise min_freq."""
del self.freq[f][key]
if not self.freq[f]:
del self.freq[f]
if self.min_freq == f:
self.min_freq = f + 1 # this bucket is empty; min freq moves up
def _bump(self, key):
"""Cache hit: move key from bucket f to the tail of bucket f+1 (most recent)."""
item = self.node[key]
f = item[2]
self._remove_from_bucket(key, f)
item[2] = f + 1
self.freq[f + 1][key] = None # tail = most recently used
def get(self, key):
if key not in self.node:
return -1
self._bump(key)
return self.node[key][0]
def _evict(self, need):
"""Evict until there is room for need: pop the head (LRU) of the min_freq bucket."""
while self.size_used + need > self.capacity and self.node:
bucket = self.freq[self.min_freq]
old_key, _ = bucket.popitem(last=False) # head = least recently used
self.size_used -= self.node[old_key][1]
del self.node[old_key]
if not bucket:
del self.freq[self.min_freq]
# next iteration continues from the new min_freq bucket if needed
def put(self, key, value, size):
if size > self.capacity:
return # single entry over capacity: ignore
if key in self.node: # update: value/size change, freq unchanged
item = self.node[key]
f = item[2]
self._remove_from_bucket(key, f) # pull out first, avoid evicting itself
self.size_used += size - item[1]
item[0], item[1] = value, size
self._evict(0) # after growing, it may overflow
self.freq[f][key] = None # back into bucket f tail (most recent)
if f < self.min_freq or not self.freq.get(self.min_freq):
self.min_freq = min(self.freq) if self.freq else f
return
self._evict(size) # insert: make room
self.node[key] = [value, size, 1]
self.freq[1][key] = None
self.size_used += size
self.min_freq = 1
The key point when updating an existing key: pull the old node out of its frequency bucket before triggering eviction so the just-updated key isn't wrongly removed; then put it back into bucket f (frequency unchanged), refresh it as most recent, and fix min_freq by the smallest remaining bucket.
Complexity: get / put are amortized O(1) (hash lookup plus head/tail operations on the bucket's OrderedDict are O(1)); evicting one node is O(1), and fixing min_freq with min(self.freq) is effectively constant when the number of buckets is small.
Prep Strategy
- Time allocation is the top priority: leave reading and edge-case time for both problems; don't grind Q1 until there's no time for Q2.
- DP: get it right, then compress space: for a counting DP like Q1, get a full 3D array to pass first, confirm correctness, then consider the rolling array — don't compress space up front and tangle yourself.
- LFU hinges on two keys: a hash map for O(1) location, and frequency buckets with in-bucket LRU for eviction; maintaining
min_freqand "update doesn't change frequency" are the two easiest traps. - Edge-case checklist: empty input, extreme values, duplicate keys, integer overflow (remember the modulo), single-entry size over capacity — walk through each in the last 10 minutes.
FAQ
Q1: How hard is the IMC SWE OA? How does it compare to LeetCode?
Two problems, 120 minutes, medium-to-hard. The algorithms aren't the hardest tier, but the emphasis is on complete engineering implementation and edge handling — the counting DP must handle pass consumption and the modulo cleanly, and the LFU must maintain frequency buckets, LRU, and capacity eviction correctly, so it's more meticulous than a pure LeetCode medium.
Q2: What platform, how long, and how many problems for the IMC SWE OA?
HackerRank, language of your choice, 120 minutes, 2 coding problems. Time is relatively generous, but both problems need modeling and edge-case work, so pacing matters.
Q3: Does Q1's pass-based path counting have to use 3D DP?
Yes, because "how many passes used" is a state dimension you must track. dp[i][j][t] is the most intuitive; after it passes, a rolling array can drop the row dimension to O(mk) space. Note that obstacles, the start, and the end all consume passes.
Q4: How does a weighted LFU differ from a plain LFU?
A plain LFU evicts by count; a weighted LFU accumulates each entry's size, and the total cannot exceed capacity, so one PUT may evict several entries to make room. Additionally, "updating an existing key doesn't change frequency" and "a single entry over capacity is ignored" are two rules specific to the weighted version.
Q5: How do I prep for an engineering-heavy OA like IMC's?
Focus on two types: DP with an extra state dimension (like the passes here), and system-simulation problems like LRU/LFU that need a hash map plus linked list. When practicing, force yourself to get the correct state before optimizing, and build the habit of a final edge-case walkthrough.
Preparing for the IMC SWE OA or another quant/trading firm's OA? We're familiar with this rhythm — HackerRank, your choice of language, two problems in 120 minutes — and can help you nail the modeling and edge cases for DP with extra state dimensions and LRU/LFU system-simulation problems, with full OA assist and OA live support.
Add WeChat Coding0201 now to get a one-on-one custom prep plan.
Contact
- WeChat: Coding0201
- Email: [email protected]
- Telegram: @OAVOProxy