Arrays, Strings & Hashing
- Flag rolling-window spend with a per-user deque and a running sum
- Count ledger runs that net to a target with a prefix-sum hash map
- Narrate, absorb the escalating follow-up, and test money and duplicate edges
The norm reported again and again for the Block coding round is plain: optimize for done. You solve the problem live in CoderPad, sitting next to the interviewer. You can Google an API you forgot. Getting the first half working beats running out of time on a clever fragment that compiles into nothing. Look past the payments surface on most of what shows up, and you are left with an array walked once with a hash map on the side.
The substitution is consistent. A stream of card charges instead of an integer array. A double-entry ledger instead of a list of numbers. A batch of payment requests instead of a string of characters. Underneath sits the same medium you already drill. What wins the round is choosing the right structure, saying why, and handling the money edges nobody asked about yet.
How Block frames arrays and hashing
The single most reported phone-screen archetype is a transaction stream with a rolling time window. Flag a user when their spend crosses a threshold in the last N seconds. Detect fraud over a moving window. Group or aggregate a feed of charges. The tooling is a sliding window: a deque plus a hash map. The grade leans on whether you can justify the deque. Expired charges leave from the front in constant time, so you never re-sum the window.
Onsite, the array work tilts toward money correctness. Reconcile a ledger that has to balance to zero. Count the date ranges that net to a settlement figure. Dedup a stream of payment requests so a retry never double-charges. Beneath the Square and Cash App labels, these are prefix-sum and hash-set problems. Once the first part runs, the interviewer layers a second requirement on top, then watches whether you refactor cleanly or start over.
- The signature screen: Rolling-window spend flag. A stream of charges per account, flag the moment a user crosses a spend limit inside the trailing window. Deque per user evicts aged charges from the front; a running sum avoids rescanning. Justifying the deque is graded as much as the code running.
- The onsite tilt: Ledger and receipt correctness. Count contiguous entries that net to a target, find a balanced batch, total an order with refunds. Prefix sum plus a hash map of counts. The edges win it: zero-sum spans, negative movements, the empty ledger.
- The hash-set move: Idempotent money movement. A payment request arrives more than once under at-least-once delivery. A seen-keys set makes the retry a no-op so the account is charged exactly once. Square's real idempotency_key API is the production form of this exact set.
- What gets scored: Narration, layering, tests. Working code on CoderPad, the reason for your structure said aloud, a graceful response when the requirement changes, and tests over money, rounding, duplicates, and invalid states. Not the single most-optimal line.
Every problem in this family is an array swept once while a hash structure remembers something about what you have already seen. The rolling-window flag remembers each user's live charges in a deque. The ledger reconciliation remembers how many times each prefix sum has occurred. The idempotent settle remembers which request keys are spent. Pick what the hash structure should hold, and the single pass falls out.
Asked at Block
Three builds in the order Block tends to ask them: the rolling-window flag from the phone screen, then the ledger reconciliation, then the idempotent settle. Write each in the editor and run your own cases, especially the ones the prompt does not spell out, before opening the walkthrough.
Rolling-Window Spend Flag: Deque + Hash Map
Problem
[time, user, amount] with time a non-decreasing integer (seconds), user an account id, and amount a positive integer (cents).Flag a charge when, after applying it, that user's total spend inside the trailing window strictly exceeds
limit. A charge belongs to the current charge's window for that user when current_time - time < window, so a charge exactly window seconds or more in the past has aged out and no longer counts.Return the list of indices (into
charges, in stream order) of the charges that trip the flag. Each user has an independent window and an independent running total.Input: charges = [[1,"A",30],[2,"A",40],[3,"B",100],[5,"A",35]], window = 3, limit = 60
Output: [1,2]
At t=2, A has 30+40=70 in the last 3s, over 60, flag index 1. At t=3, B alone is 100, flag index 2. At t=5, A's charges from t=1 and t=2 have aged out, leaving only 35, under the limit.
Input: charges = [[1,"A",50],[2,"A",10],[10,"A",55]], window = 5, limit = 55
Output: [1]
At t=2, A has 60 in window, over 55, flag index 1. At t=10 the earlier two charges are gone and 55 does not strictly exceed 55, so no flag.
Keep one deque of (time, amount) per user plus that user's running sum. On each charge, push it and add to the sum, then pop from the front while the front charge has aged out of the window, subtracting as you go. After eviction, compare the running sum to the limit. The deque gives O(1) front eviction, which is why you never re-sum the window.
Rolling-Window Spend Flag: Deque + Hash Map
Click "Run" to execute your code
Test Cases
[[1,"A",30],[2,"A",40],[3,"B",100],[5,"A",35]], 3, 60Expected: [1, 2][[1,"A",50],[2,"A",10],[10,"A",55]], 5, 55Expected: [1][[1,"A",40],[2,"B",40],[3,"A",30],[4,"B",30]], 10, 60Expected: [2, 3][], 3, 60Expected: [](time, amount, merchant). Next to the running sum, keep a small hash map of merchant to live count for that user. Increment on push, decrement on eviction, and drop a merchant when its count hits zero. The flag condition becomes the sum check and the map's size, both already maintained in one pass. (Discussed in the solution.)Ledger Reconciliation: Prefix Sum + Hash Map
Problem
movements, a list of signed integers in posting order, where each value is the net effect of one entry in cents (credits positive, debits negative).Reconciliation asks: how many contiguous spans of entries net to exactly
target? A span is a run movements[i..j] for i <= j, and a span that nets to target is a self-contained balanced batch (use target = 0 for a batch that fully cancels out). Spans that overlap are counted separately.Return the count of contiguous spans summing to
target.Input: movements = [10,-10,30,-20,-10], target = 0
Output: 4
The zero-netting spans are [10,-10], [30,-20,-10], [10,-10,30,-20,-10], and the run from index 1 to 3 (-10,30,-20). Four in total.
Input: movements = [100,-50,-50,100], target = 100
Output: 3
The single entry at index 0, the single entry at index 3, and the whole array all net to 100.
Walk a running prefix sum. A span ending at the current entry nets to target exactly when some earlier prefix equals current_prefix minus target. Keep a hash map from prefix value to how many indices produced it, seeded with {0: 1} for spans that start at index 0. Add its count to the answer before recording the current prefix.
Ledger Reconciliation: Prefix Sum + Hash Map
Click "Run" to execute your code
Test Cases
[10,-10,30,-20,-10], 0Expected: 4[100,-50,-50,100], 100Expected: 3[1,2,3], 3Expected: 2[], 0Expected: 0Idempotent Settle: Hash-Set Dedup
Problem
requests, a list of [key, amount]. The key is the client's idempotency key for that payment and amount is a non-negative integer (cents). Because delivery is at-least-once, the same key can appear more than once: those repeats are retries of one logical payment.Apply each request in order. The first time a
key is seen, charge its amount. Any later request with a key already applied is a retry and must be a no-op, so the account is charged exactly once per key.Return the total amount charged.
Input: requests = [["a",10],["b",5],["a",10],["c",7]]
Output: 22
Keys a, b, c each charge once for 10+5+7=22. The second ["a",10] is a retry and is ignored.
Input: requests = [["a",10],["a",999]]
Output: 10
The key a is already applied, so the retry is a no-op regardless of its amount. Only the first charge of 10 counts.
Keep a set of keys already applied. For each request, if the key is in the set, skip it. Otherwise add the key and add its amount to the total. Membership in a hash set is O(1), so the whole pass is linear.
Idempotent Settle: Hash-Set Dedup
Click "Run" to execute your code
Test Cases
[[["a",10],["b",5],["a",10],["c",7]]]Expected: 22[[["a",10],["a",999]]]Expected: 10[[]]Expected: 0[[["x",0],["y",8],["x",0],["x",0]]]Expected: 8Fraud Velocity Check: Distinct Devices in a Rolling Window
Problem
i arrives as three parallel columns: times[i] is a non-decreasing integer timestamp in seconds, accounts[i] is the account id, and devices[i] is the device fingerprint the event came from.Flag event
i when, after applying it, the number of distinct devices that account has used inside the trailing window is at least threshold. An earlier event of the same account is still live for the current one when times[i] - times[j] < window, so an event exactly window seconds or more in the past has aged out and stops counting.Return the list of indices of the flagged events in stream order. Each account owns its own window and its own device count, and repeat traffic from a device already inside the window does not raise that account's distinct total.
Input: times = [1,2,3,9], accounts = ["A","A","A","A"], devices = ["d1","d2","d1","d3"], window = 5, threshold = 2
Output: [1,2]
At t=2 account A is live on d1 and d2, so two distinct devices trips index 1. At t=3 the repeat of d1 holds the count at two, which trips index 2 as well. By t=9 the first three events have aged out and only d3 is live, leaving one device.
Input: times = [1,2,3,4,20], accounts = ["A","A","A","A","A"], devices = ["d1","d2","d2","d3","d4"], window = 5, threshold = 3
Output: [3]
The second d2 adds no new device, so A sits at two distinct devices until index 3 brings d3 and reaches three. At t=20 every earlier event has expired and only d4 is live.
Per account, keep a deque of (time, device) alongside a map from device to how many live events used it. On each event, push onto the back and increment that device. Then pop from the front while the front has aged out, decrementing its device and deleting the key once it reaches zero. The size of that map is the distinct count, so one comparison decides the flag.
Fraud Velocity Check: Distinct Devices in a Rolling Window
Click "Run" to execute your code
Test Cases
[1,2,3,9], ["A","A","A","A"], ["d1","d2","d1","d3"], 5, 2Expected: [1, 2][1,2,3,4,20], ["A","A","A","A","A"], ["d1","d2","d2","d3","d4"], 5, 3Expected: [3][1,2,3,4], ["A","A","A","A"], ["d1","d1","d1","d1"], 10, 2Expected: [][1,1,2,2,3], ["A","B","A","B","A"], ["d1","d2","d1","d2","d9"], 10, 2Expected: [4][0,5], ["A","A"], ["d1","d2"], 5, 2Expected: [][0,5], ["A","A"], ["d1","d2"], 6, 2Expected: [1][], [], [], 5, 2Expected: []k events, so eviction fires when the deque grows past k and the timestamp stops mattering, while the device counting stays exactly as it was. Then track the largest distinct-device count each account's window ever reached, so a risk review can rank accounts without replaying the stream. Both changes live in the same loop. (Discussed in the solution.)Daily Payout Rollup: Group a Transaction Stream by Seller
Problem
records, a list of rows in feed order. Each row is [recordId, seller, date, kind, amountCents].-
recordId is the feed's id for that row. Delivery is at-least-once, so the same recordId can arrive more than once, and a repeat is a redelivery of one row rather than new money.-
seller is the seller the money belongs to, and date is the settlement date as YYYY-MM-DD, already converted to that seller's local day.-
kind is one of "charge", "refund", "fee", and amountCents is a non-negative integer.Roll the feed up into one net figure per (seller, date) bucket, where a charge adds and a refund or a fee subtracts. Every bucket holding at least one row appears in the output, including a bucket that comes out at 0 or below.
Return the buckets as a list of
[seller, date, netCents], sorted by seller ascending and then by date ascending.Input: records = [["r1","acme","2026-03-01","charge",5000],["r2","acme","2026-03-01","fee",150],["r3","acme","2026-03-01","refund",1200],["r4","bodega","2026-03-01","charge",900],["r5","acme","2026-03-02","charge",2500]]
Output: [["acme","2026-03-01",3650],["acme","2026-03-02",2500],["bodega","2026-03-01",900]]
acme nets 5000 - 150 - 1200 = 3650 on March 1 and 2500 on March 2. bodega has a single charge on March 1. Seller sorts first, so both acme days precede bodega.
Input: records = [["r1","acme","2026-03-01","charge",5000],["r1","acme","2026-03-01","charge",5000],["r2","acme","2026-03-02","refund",5000]]
Output: [["acme","2026-03-01",5000],["acme","2026-03-02",-5000]]
The second r1 is a redelivery, so March 1 still settles 5000. The refund carries its own date, which puts it on March 2 and leaves that day at -5000 instead of cancelling March 1.
Input: records = [["r9","kiosk","2026-01-31","fee",300]]
Output: [["kiosk","2026-01-31",-300]]
A day of fees with no sales behind them settles negative, which is a real payout the seller owes back rather than an error to clamp at zero.
One pass with a hash map keyed by the (seller, date) pair, plus a set of recordIds already folded in. Skip a row whose id is in the set, otherwise record the id and add the amount with a sign picked from kind. Flatten the map into rows at the end and sort them, since a hash map hands back its keys in whatever order it likes.
Daily Payout Rollup: Group a Transaction Stream by Seller
Click "Run" to execute your code
Test Cases
[[["r1","acme","2026-03-01","charge",5000],["r2","acme","2026-03-01","fee",150],["r3","acme","2026-03-01","refund",1200],["r4","bodega","2026-03-01","charge",900],["r5","acme","2026-03-02","charge",2500]]]Expected: [["acme","2026-03-01",3650],["acme","2026-03-02",2500],["bodega","2026-03-01",900]][[["r1","acme","2026-03-01","charge",5000],["r1","acme","2026-03-01","charge",5000],["r2","acme","2026-03-02","refund",5000]]]Expected: [["acme","2026-03-01",5000],["acme","2026-03-02",-5000]][[["r9","kiosk","2026-01-31","fee",300]]]Expected: [["kiosk","2026-01-31",-300]][[]]Expected: [][[["a1","zebra","2026-02-01","charge",1000],["a2","zebra","2026-02-01","refund",1000],["a3","acorn","2026-12-31","charge",10],["a4","acorn","2026-02-01","charge",20]]]Expected: [["acorn","2026-02-01",20],["acorn","2026-12-31",10],["zebra","2026-02-01",0]][[["d1","mesa","2026-05-10","charge",700],["d2","mesa","2026-05-10","refund",250],["d2","mesa","2026-05-10","refund",250],["d3","mesa","2026-05-11","fee",100],["d1","mesa","2026-05-10","charge",700]]]Expected: [["mesa","2026-05-10",450],["mesa","2026-05-11",-100]]Key Takeaways
- ✓Block frames arrays and hashing as money movement: a charge stream instead of an integer array, a double-entry ledger instead of a list of numbers, a batch of payment requests instead of a string. The algorithm underneath is the recognizable medium you already practice. Read past the framing and reach for the structure.
- ✓The signature is a single sweep with a hash structure holding what you've seen. A per-user deque of live charges for the rolling-window flag. A prefix-sum count map for ledger reconciliation. A set of applied keys for idempotent settle. Decide what the hash structure remembers and the one pass falls out.
- ✓It is a pairing round, so talk. You can Google an API, getting to working code beats a clever fragment, and they grade narration and your response to the requirement they stack on as much as the answer. Say why the deque evicts in O(1), then take the follow-up without rewriting from scratch.
- ✓Test the money edges the prompt did not mention, since Block rewards covering rounding, duplicates, and invalid states unprompted, then stop before over-engineering. A retry that double-charges or a ledger span miscounted by one is the failure these problems are built to expose.
Block interview guideKnow what Block asks before you walk in.
13 coding · 5 system design · 14 behavioral questions
Get their questions first.
InterviewLegend is independent and not affiliated with or endorsed by any company it covers.