Nearest Two Words
Problem
A search tool ranks a document higher when two query words appear close together. Given a list of words and two different words a and b that each appear at least once, return the smallest distance between a position holding a and a position holding b. The distance between positions i and j is the absolute value of i minus j.
Examples
Input: words = ["cat", "dog", "fox", "cat", "owl", "dog"], a = "cat", b = "dog"
Output: 1
Why: the cat at 0 and the dog at 1 are neighbours
Input: words = ["red", "sky", "sky", "sky", "blue"], a = "blue", b = "red"
Output: 4
Why: each word appears once, at opposite ends
Input: words = ["up", "down"], a = "up", b = "down"
Output: 1
Why: edge case, the shortest list that can hold both words
Hints
0 / 3
Comparing every position of a with every position of b works, but it is quadratic when both words are common.
When you reach an a, the only b that can give it the best distance so far is the most recent b before it, and the same holds the other way round.
Scan once and remember the latest position of a and the latest position of b. Each time you land on either word and both have been seen, compare their distance with the best so far.
Solution
For a pair of positions, the later one is met during the scan while the earlier one is still stored, as long as no copy of the same word came between them. If another copy did come between, that copy is closer to the later position, so the skipped pair could never have been the best. Checking the distance only when one of the two words is seen keeps the pass cheap. Time is O(n) and space is O(1).
def nearest_pair(words, a, b):
last_a = last_b = None # latest position of each word so far
best = len(words)
for i, w in enumerate(words):
if w == a:
last_a = i
elif w == b:
last_b = i
else:
continue # other words cannot change the answer
if last_a is not None and last_b is not None:
best = min(best, abs(last_a - last_b))
return best
print(nearest_pair(["cat", "dog", "fox", "cat", "owl", "dog"], "cat", "dog")) # -> 1
print(nearest_pair(["red", "sky", "sky", "sky", "blue"], "blue", "red")) # -> 4
print(nearest_pair(["up", "down"], "up", "down")) # -> 1Stuck on the idea rather than the code? Linear Search covers it.