from collections import defaultdict
class WordDistance:
def __init__(self, wordsDict):
self.word_indices = defaultdict(list)
for i, word in enumerate(wordsDict):
self.word_indices[word].append(i)
def shortest(self, word1, word2):
indices1 = self.word_indices[word1]
indices2 = self.word_indices[word2]
i, j = 0, 0
min_dist = float('inf')
while i < len(indices1) and j < len(indices2):
min_dist = min(min_dist, abs(indices1[i] - indices2[j]))
if indices1[i] < indices2[j]:
i += 1
else:
j += 1
return min_dist
class WordDistance {
public:
unordered_map<string, vector<int>> word_indices;
WordDistance(vector<string>& wordsDict) {
for (int i = 0; i < wordsDict.size(); ++i) {
word_indices[wordsDict[i]].push_back(i);
}
}
int shortest(string word1, string word2) {
const vector<int>& indices1 = word_indices[word1];
const vector<int>& indices2 = word_indices[word2];
int i = 0, j = 0, min_dist = INT_MAX;
while (i < indices1.size() && j < indices2.size()) {
min_dist = min(min_dist, abs(indices1[i] - indices2[j]));
if (indices1[i] < indices2[j]) ++i;
else ++j;
}
return min_dist;
}
};
class WordDistance {
Map<String, List<Integer>> wordIndices;
public WordDistance(String[] wordsDict) {
wordIndices = new HashMap<>();
for (int i = 0; i < wordsDict.length; i++) {
wordIndices.computeIfAbsent(wordsDict[i], k -> new ArrayList<>()).add(i);
}
}
public int shortest(String word1, String word2) {
List<Integer> indices1 = wordIndices.get(word1);
List<Integer> indices2 = wordIndices.get(word2);
int i = 0, j = 0, minDist = Integer.MAX_VALUE;
while (i < indices1.size() && j < indices2.size()) {
minDist = Math.min(minDist, Math.abs(indices1.get(i) - indices2.get(j)));
if (indices1.get(i) < indices2.get(j)) i++;
else j++;
}
return minDist;
}
}
class WordDistance {
constructor(wordsDict) {
this.wordIndices = {};
for (let i = 0; i < wordsDict.length; i++) {
if (!(wordsDict[i] in this.wordIndices)) {
this.wordIndices[wordsDict[i]] = [];
}
this.wordIndices[wordsDict[i]].push(i);
}
}
shortest(word1, word2) {
const indices1 = this.wordIndices[word1];
const indices2 = this.wordIndices[word2];
let i = 0, j = 0, minDist = Infinity;
while (i < indices1.length && j < indices2.length) {
minDist = Math.min(minDist, Math.abs(indices1[i] - indices2[j]));
if (indices1[i] < indices2[j]) i++;
else j++;
}
return minDist;
}
}
You are given a list of words called wordsDict
. You need to design a class WordDistance
that supports two operations:
wordsDict
array.word1
and word2
, return the shortest distance between any occurrence of word1
and word2
in the list. The distance is the absolute difference of their indices.shortest
method may be called multiple times, so your solution should be efficient for repeated queries.
At first glance, the problem seems similar to finding the minimum distance between two elements in an array. The brute-force approach would be, for each query, to scan the entire list and compare every occurrence of word1
with every occurrence of word2
, keeping track of the minimum distance.
However, since the shortest
method may be called many times, this would be inefficient. We need a way to process queries quickly. This suggests preprocessing the list to store the positions of each word, so that looking up all positions of a word is fast. Then, the problem reduces to finding the minimum difference between two sorted lists of indices, which can be solved efficiently using a two-pointer technique.
To efficiently support repeated queries, we use the following steps:
wordsDict
once.This approach ensures fast initialization and very fast queries, even if there are many.
Let's consider wordsDict = ["practice", "makes", "perfect", "coding", "makes"]
and a query for word1 = "coding"
, word2 = "practice"
.
word1 = "makes"
, word2 = "coding"
:
Brute-force Approach:
wordsDict
to find all occurrences of both words and compare every pair.wordsDict
, to build the hash map.word1
and word2
in the list (since we step through both lists once).The optimized approach is much faster for repeated queries.
By preprocessing the word list to store all indices for each word, we can answer multiple shortest-distance queries efficiently. The two-pointer technique leverages the naturally sorted order of indices, making each query much faster than a brute-force search. This design is elegant because it separates preprocessing from querying, and each part is optimal for its role.