Never Walk Alone
An easy Python interview practice problem on DataDriven. Write the Python and run it against test cases, with instant feedback.
- Domain
- Python
- Difficulty
- easy
- Seniority
- L3
- Asked in
- JPMorgan Chase
Problem
Which adjacent word pairs does a raw search query in text contain, in the order they appear? Queries come straight from the request log, so words can be separated by tabs or runs of spaces, and that spacing never forms a word of its own.
Every step is measured against the one before it.
Example 1
Input:
text = "the quick brown fox"
Output:
[["the","quick"],["quick","brown"],["brown","fox"]]
Example 2
Input:
text = "hello world"
Output:
[["hello","world"]]
Worked solution and explanation
Strip the costume and this is bigram extraction: every adjacent pair of tokens, overlapping, from text whose spacing you do not control. The pairing is the easy part. The problem lives in two decisions that look cosmetic. The first is tokenizing. text.split(' ') treats each single space as a delimiter, so a tab stays glued inside a token, a double space manufactures an empty word, and 'sun\tmoon stars' comes back with pairs no grader accepts. The second is the bound. Walk to len(words) instead of len(words) - 1 and the last step reads words[i + 1] off the end. That is an IndexError on exactly the boundary input an interviewer hands you.
How a senior reasons through it
Step 1: Tokenize on any whitespace run
words = text.split(). Pick it deliberately over split(' '): the log has tabs and double spaces, and the bare call makes runs of whitespace a non-issue instead of a bug you patch later with an if word filter.
Step 2: Pair each word with its right-hand neighbor
range(len(words) - 1) gives every start index that has a neighbor. Each interior word appears twice, as the right of one pair and the left of the next. Stepping two at a time instead silently drops every pair that straddles a chunk boundary.
Step 3: Let the bound absorb short inputs
No special case for an empty or one-word query. With zero or one word, len(words) - 1 is -1 or 0, the range is empty, the loop never runs, and the function returns []. The edge is handled by the bound, not by a guard you could forget.
def sequential_word_pairs(text):
words = text.split()
result = []
for i in range(len(words) - 1):
pair = [words[i], words[i + 1]]
result.append(pair)
return resultBare split() to tokenize; range(len(words) - 1) makes the short-input case disappear.
- Literal single-space split
'sun\tmoon stars'.split(' ')gives['sun\tmoon', '', 'stars']. The tab stays inside a token and the double space yields ''. An empty query gives[''], which has length 1, not 0.- Bare whitespace split
'sun\tmoon stars'.split()gives['sun', 'moon', 'stars']. Runs collapse, edges trim, and''.split()is[]. The line of code is no longer, and none of the failure modes remain.
Common follow-up questions
- How would you generalize this to arbitrary n-grams? (Loop over `range(len(words) - n + 1)` and slice `words[i:i + n]`. This tests whether the bound generalizes.)
- What if you needed the count of each unique pair instead of the list? (Use a `Counter` keyed by `tuple(pair)`. Lists are not hashable, so the tuple conversion is the tell.)
- How would you handle punctuation stuck to words, like 'fox.'? (Tokenize with a regex such as `\w+`, or strip punctuation with `str.translate` first. That turns whitespace splitting into real tokenization.)