InterviewLegend

Arrays, Strings & Hashing

Alex Chen30 min readUpdated 36 real interview reports
In this lesson
  • Use a hash map to turn a re-scan into one pass over a stream of events
  • Spot aggregation, carry-forward state and string matching, the three shapes Palantir reuses here
  • Build the three most-reported problems, holdings, non-zero balances and subsequence matching, with the code run live

Arrays, strings, and hashing are the bread and butter of Palantir's live coding round. The single most-repeated problem in the whole loop lives right here: a stock-portfolio question that asks you to carry a set of holdings forward across dates. The round is a medium to medium-hard problem in a shared editor where the code runs. A working answer with the edge cases handled beats a clever one that fails a case. What ties the family together is the same instinct every time. You hold onto what you have already seen, netted, or counted in a hash map, and one pass answers a question a re-scan would answer slowly.

The Palantir texture is that these problems arrive wrapped in a story about real data. Field records, transaction ledgers, share positions over time. Part of the grade is pulling the actual specification out of that narrative before you touch the keyboard, then narrating the plan as you build. Forward Deployed Engineers spend their days wrangling a customer's messy data into something a platform like Foundry can reason over, so the round is a proxy for that work. Read the story, name the data model, reach for the map, and say why as you go.

How Palantir frames it

Three shapes carry nearly every array-and-hashing problem in this round. The first is aggregation: you walk a pile of records once and net or tally them into a map keyed by whatever the question groups on, an account, a ticker, a category. The second is carry-forward state, where you sweep an event timeline in order and keep a running value that persists across gaps, which is exactly the portfolio-holdings pattern. The third is string matching, where two pointers or a map of character positions decides whether one string sits inside another. All three trade a little memory for a single linear pass. Spot which shape you are in early, because that read is half the round.

The Palantir angle
The daily-holdings problem is the one you should expect by name. Candidate reports put a stock-portfolio question at the center of Palantir phone screens more than any other. You get per-company share events over a range of dates, and you report the total position held on each day, carrying an unchanged position forward on quiet days. It is a hash map keyed by date or by ticker plus a running total, nothing exotic. What gets graded is whether you state the carry-forward rule before coding and handle a day with no events cleanly. Name the model, then build it, and keep the running state tidy when the interviewer stretches the date range from days to years.

A hash map is the cost-cut. Almost every problem in this family has a brute-force version that re-scans the data for each answer, and the map trades a little memory to collapse that inner loop into a lookup. A map from key to its running net, its count, or its last position turns a quadratic re-scan into one linear pass. The interviewer watches for you reaching for it and naming the trade. The code that follows is the easy part.

The core moves

Hash-map aggregation over messy records

The workhorse move is to walk a list of records once and fold them into a map. Counting tallies how often each key appears. Netting adds and subtracts signed amounts so the final value is the balance for that key. Both read the same in code: for each record, look up the key, apply the change, move on. The financial-ledger problems Palantir favors are pure netting, where money leaves one account and lands on another and you want the keys that did not settle back to zero. Same loop, different verb.

python
# Netting: fold signed amounts into a running balance per key
net = {} # key -> running balance
for src, dst, amount in transfers:
net[src] = net.get(src, 0) - amount # money leaves the source
net[dst] = net.get(dst, 0) + amount # money lands at the destination
unsettled = [k for k, bal in net.items() if bal != 0] # keys that did not net to zero

The reason this beats re-scanning is that every record touches its key exactly once, so the whole aggregation is linear in the number of records. The same fold answers "how many," "how much," and "which keys are left over" by changing only what you store in the map. When the question asks you to split the tally along a second dimension, you widen the key rather than rebuild the loop. That is the door the follow-up on the ledger problem walks through.

Carry-forward state over a timeline

Some problems hand you dated events and ask for a value on every day, not just the days something happened. Sweep time in order while holding a running state that persists across empty stretches. A position you bought on Monday is still held on Tuesday even though Tuesday has no trade, so the running total simply carries. Group the events by day first, then walk the calendar, applying each day's changes and recording the total as you pass. The state never resets between days, which is what makes the gaps fill themselves in.

python
# Carry-forward: sweep days in order, running total persists across empty days
by_day = {}
for day, key, delta in events:
by_day.setdefault(day, []).append((key, delta))
total = 0 # running value that carries across gaps
for day in range(min_day, max_day + 1):
for key, delta in by_day.get(day, []):
total += delta # apply this day's changes
record(day, total) # a day with no events reuses the carried total

The subtle part is the empty day. Because the total lives outside the loop over a single day's events, a day with nothing in by_day records the same value it held yesterday, which is the carry-forward the problem wants. Walking every calendar day is fine when the range is small. When the range grows to years with only a handful of events, that full sweep becomes the bottleneck, and that is precisely the escalation the interviewer reaches for.

Two-pointer and index-map string matching

The string questions here ask whether one sequence sits inside another. Two pointers is the base tool: advance through the text, ticking off the query's characters in order, and you have matched the whole query the moment the query pointer reaches the end. That single walk is linear in the length of the text. Many queries change the calculus. Now you preprocess once, mapping each character to the sorted list of positions where it occurs, then binary-search the next legal position per query character.

python
# Two pointers: is the query a subsequence of the text?
i = 0 # position in the query
for ch in text:
if i < len(q) and ch == q[i]:
i += 1 # matched the next query character
matched = (i == len(q)) # true only if every query char was found in order
The Palantir angle
Expect a follow-up before you have finished the first answer. The reported pattern is a two-pointer string match followed immediately by "now handle many queries against the same text" or "now the query is more than one word." The base and the escalation share the same idea, so the graded judgment is whether you notice that preprocessing the text pays off once you are answering repeatedly rather than once. For a Forward Deployed Engineer matching abbreviated tokens against long field records, that read-once-then-answer-many shape is the day job, which is why the round leans on it.

Asked at Palantir

Three problems, in the order the round tends to reward them. Daily portfolio holdings drills the carry-forward sweep and the sparse-range follow-up it is built around. Non-zero-balance accounts drills the netting fold, with a classification twist that mirrors how Palantir handles marked data. Query subsequence matching drills the two-pointer walk and the many-queries escalation. Work each one in the editor before you open the walkthrough. State the complexity of your plan as you build, because the round grades the narration as much as the code. The code runs against the cases, so surface your edge cases before they surface you.

1

Daily Portfolio Holdings: Carry-Forward Over Dates

MediumSignature problem
Problem
A trading account records share events over time. Each event is [day, ticker, shares], where shares is positive for a buy and negative for a sell. Positions persist: shares bought on one day are still held on later days until sold.

Return the total number of shares held across all tickers at the end of every day from the earliest event day to the latest event day, inclusive. A day with no events carries the previous day's total forward.

Return a list of [day, totalShares] pairs, one per day in that range, in day order.

Input encoding. Each test case is a JSON array [events], where events is the list of [day, ticker, shares] records. The output is the list of [day, totalShares] pairs.

Input: events = [[1,"AAPL",10],[1,"MSFT",5],[3,"AAPL",-4]]

Output: [[1,15],[2,15],[3,11]]

Day 1 buys 10 AAPL and 5 MSFT for a total of 15. Day 2 has no events, so the total carries to 15. Day 3 sells 4 AAPL, leaving 6 AAPL and 5 MSFT, a total of 11.

Input: events = [[1,"A",10],[2,"A",-10]]

Output: [[1,10],[2,0]]

Day 1 holds 10 shares. Day 2 sells all 10, so the total is 0.

How this uses the pattern

Group the events by day, then sweep from the first day to the last while keeping one running total. A day with no events records the same total as the day before, which is the carry-forward.

Daily Portfolio Holdings: Carry-Forward Over Dates

Loading...
Click "Run" to execute your code

Test Cases

Input: [[[1,"AAPL",10],[1,"MSFT",5],[3,"AAPL",-4]]]Expected: [[1,15],[2,15],[3,11]]
Input: [[[5,"X",3]]]Expected: [[5,3]]
Input: [[[1,"A",10],[2,"A",-10]]]Expected: [[1,10],[2,0]]
Input: [[[2,"A",4],[2,"B",6],[4,"A",-1],[5,"B",-6]]]Expected: [[2,10],[3,10],[4,9],[5,3]]
Input: [[[10,"AAPL",100],[10,"AAPL",-100],[11,"GOOG",50]]]Expected: [[10,0],[11,50]]
The Palantir angle
This is the single most-repeated Palantir coding problem, and it usually opens a phone screen framed as a story about holdings over time. You are graded on whether you state the carry-forward rule before writing code and handle a day with no activity cleanly. Interviewers stretch the date range mid-round to see whether your running state survives the change. Model the positions as a hash map from ticker to shares, keep a running total alongside it, and narrate why the total persists across an empty day rather than resetting.
Take it further
The events now span years, but there are only a few dozen of them. Walking every calendar day wastes almost all of its work on days where nothing changes. Return the compressed timeline instead: one entry per day the total changes. Sort the distinct event days, net each day's deltas, and emit a point only where the running total moves. The compressed version is worked below and is what the sparse-range follow-up wants.
2

Non-Zero-Balance Accounts: Netting Over a Ledger

EasyFrequently asked
Problem
A ledger records money moving between accounts. Each entry is [source, destination, amount], meaning amount leaves source and arrives at destination.

Return the list of account IDs whose net balance is not zero after all entries are applied, sorted in ascending order. An account that receives exactly what it sends nets to zero and is excluded.

Input encoding. Each test case is a JSON array [transfers], where transfers is the list of [source, destination, amount] entries. The output is the sorted list of account IDs with a non-zero net balance.

Input: transfers = [["A","B",30],["B","A",30],["A","C",5]]

Output: ["A", "C"]

A nets -30 + 30 - 5 = -5, B nets +30 - 30 = 0, C nets +5. B settled to zero, so only A and C remain.

Input: transfers = [["A","B",50],["B","A",50]]

Output: []

A and B each send and receive 50, so both net to zero and nothing is returned.

How this uses the pattern

Fold the transfers into a map from account to running balance, subtracting from the source and adding to the destination. Then keep the keys whose balance is not zero and sort them.

Non-Zero-Balance Accounts: Netting Over a Ledger

Loading...
Click "Run" to execute your code

Test Cases

Input: [[["A","B",30],["B","A",30],["A","C",5]]]Expected: ["A","C"]
Input: [[["A","B",50],["B","A",50]]]Expected: []
Input: [[["A","B",30],["B","C",10]]]Expected: ["A","B","C"]
Input: [[["X","Y",100]]]Expected: ["X","Y"]
Input: [[["A","B",10],["C","B",10],["B","A",10],["B","C",10]]]Expected: []
The Palantir angle
Palantir's financial-analysis prompts lean on this netting fold, often narrated as reconciling a messy transaction feed. The tell is the zero boundary: an account touched by many transfers can still settle to exactly zero, and the ask is only the ones that did not. State that a balanced account is excluded before you filter, because that is the line the hidden cases probe. The fold is linear and the whole problem is really about reading the spec precisely, which is the FDE data-reconciliation skill in miniature.
Take it further
Each transfer now carries a classification marking, and balances must never net across markings. A dollar moved under one marking cannot cancel a dollar under another, so netting has to stay within a marking. Widen the map key from the account alone to the pair of account and marking, then report the [account, marking] pairs that did not settle. Keying by the pair keeps every classification on its own ledger. The marked version is worked below.
3

Query Subsequence Match: Two Pointers, Then Many Queries

MediumCommonly asked
Problem
A record is a string text. A query q matches the record if every character of q appears in text in the same order, though not necessarily next to each other. In other words, q is a subsequence of text.

Return true if q is a subsequence of text, and false otherwise. The empty query matches any record.

Input encoding. Each test case is a JSON array [text, q] of two strings. The output is a boolean.

Input: text = "foundry", q = "fry"

Output: true

The characters f, r, y appear in "foundry" in that order (positions 0, 5, 6), so the query matches.

Input: text = "gotham", q = "gothamx"

Output: false

The query needs a trailing x that "gotham" does not contain, so it cannot match.

How this uses the pattern

Two pointers. Walk the text once, advancing a query pointer whenever the current text character matches the query character it points at. The query is a subsequence exactly when that pointer reaches the end.

Query Subsequence Match: Two Pointers, Then Many Queries

Loading...
Click "Run" to execute your code

Test Cases

Input: ["foundry","fry"]Expected: true
Input: ["gotham","gothamx"]Expected: false
Input: ["abcde","ace"]Expected: true
Input: ["abcde","aec"]Expected: false
Input: ["palantir",""]Expected: true
Input: ["","x"]Expected: false
The Palantir angle
Candidate reports describe a two-pointer string-match on a screen, followed almost at once by "now handle many queries" or "now the query has more than one word." The base is a clean linear walk, so the interesting judgment is what you do when the same record is matched over and over. Say up front that a single walk is linear in the text and ideal for one query, then flag that repeated matching against an unchanging record is worth preprocessing. That read-once-answer-many instinct is exactly the shape a Forward Deployed Engineer needs against a large, static field corpus.
Take it further
Now many queries hit the same fixed text, and it is far longer than any query. Re-walking the whole text per query wastes work when the text never changes. Preprocess once: map each character to the sorted list of positions where it occurs. For each query, track your current position and binary-search the next occurrence of the next character strictly after it. Each query then costs its own length times a log factor, independent of the text length. The many-queries version is worked below.

Key Takeaways

  • ✓A hash map is the default cost-cut on the Palantir algorithm round. Aggregation nets or counts records into a map in one pass, carry-forward state sweeps an event timeline while a running value persists across gaps, and string matching walks two pointers or an index map. Reach for the map and name the trade as you do.
  • ✓Daily portfolio holdings is the problem to expect by name. Group events by day, sweep the calendar with a running total that never resets between days, and the carry-forward across an empty day falls out for free. When the range grows to years, compress to change-points only.
  • ✓The round grades the narration and the edge cases as much as the answer, because the code runs live. State the carry-forward rule, the zero-balance exclusion, and the empty-query case before the hidden cases reach them, and pull the real spec out of the story before you write a line.
  • ✓Build for the follow-up, because it arrives fast. A sparse date range wants change-points, a classification marking widens the map key, and a stream of queries against a fixed record wants a one-time index. How cleanly you take the escalation is what separates a pass from a strong pass.
Had your Palantir interview?Tell us what they asked. Reviewed before it goes in, never with your name.
Palantir interview guide

Know what Palantir asks before you walk in.

20 coding · 6 system design · 17 behavioral questions

Get Premium$49 a year · Cancel anytime
Or start freeNo card.

Get their questions first.

InterviewLegend is independent and not affiliated with or endorsed by any company it covers.