243. Shortest Word Distance 🔒
Problem Description
You are given an array of strings wordsDict
and two strings word1
and word2
that both exist in the array. Your task is to find the shortest distance between these two words in the array.
The distance between two words is calculated as the absolute difference between their indices in the array. For example, if word1
appears at index i
and word2
appears at index j
, the distance between them is |i - j|
.
Since the same word can appear multiple times in the array, there might be multiple pairs of positions for word1
and word2
. You need to find the minimum distance among all possible pairs.
Example:
- If
wordsDict = ["practice", "makes", "perfect", "coding", "makes"]
,word1 = "coding"
, andword2 = "practice"
"coding"
is at index 3 and"practice"
is at index 0- The distance is
|3 - 0| = 3
The solution uses a single pass through the array, keeping track of the most recent positions of both word1
and word2
. Whenever either word is encountered, it updates the corresponding position and calculates the distance if both words have been seen at least once. The minimum distance is maintained throughout the traversal.
Intuition
The key insight is that we only need to track the most recent occurrence of each word as we traverse the array. Why? Because the minimum distance will always involve consecutive occurrences of the two different words.
Think about it this way: if we have positions of word1
at indices [2, 5, 8]
and word2
at indices [1, 4, 7]
, the minimum distance won't be between word1
at index 2 and word2
at index 7. It will be between adjacent pairs like (2,1)
, (5,4)
, or (8,7)
.
This is because as we move through the array linearly, whenever we encounter one of our target words, the closest occurrence of the other word must be the one we saw most recently. Any earlier occurrence would be farther away.
For example, if we're at position 5 and find word1
, and we last saw word2
at position 4, the distance is |5-4| = 1
. Even if word2
also appeared at position 1, that distance |5-1| = 4
would be larger.
This observation allows us to solve the problem in a single pass with O(1)
space. We maintain two variables i
and j
to store the most recent positions of word1
and word2
respectively. Each time we find either word, we update its position and calculate the distance if we've seen both words at least once (indicated by both i
and j
not being -1). We keep track of the minimum distance found so far, which becomes our answer.
Solution Approach
We implement a single-pass algorithm using two pointer variables to track the most recent positions of each word:
-
Initialize tracking variables: Set
i = j = -1
to indicate that neither word has been found yet. Initializeans = inf
to store the minimum distance. -
Traverse the array: Use
enumerate()
to iterate throughwordsDict
with both indexk
and wordw
. -
Update positions:
- When we encounter
word1
, updatei = k
to store its latest position - When we encounter
word2
, updatej = k
to store its latest position
- When we encounter
-
Calculate distance: After each update, check if both words have been found (
i != -1 and j != -1
). If so, calculate the distance asabs(i - j)
and update the minimum:ans = min(ans, abs(i - j))
. -
Return result: After traversing the entire array, return
ans
which holds the minimum distance found.
The algorithm works because at any point during traversal, variables i
and j
hold the most recent positions of word1
and word2
respectively. Since we're looking for the minimum distance, we only need to consider the closest pair, which will always involve the most recent occurrence of one word with the most recent occurrence of the other.
Time Complexity: O(n)
where n
is the length of wordsDict
, as we make a single pass through the array.
Space Complexity: O(1)
as we only use a constant amount of extra space for variables i
, j
, and ans
.
Ready to land your dream job?
Unlock your dream job with a 3-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a small example to illustrate the solution approach.
Input: wordsDict = ["the", "quick", "brown", "fox", "quick"]
, word1 = "the"
, word2 = "quick"
Step-by-step execution:
Initially: i = -1
, j = -1
, ans = infinity
Index 0: word = "the"
- Found
word1
! Updatei = 0
- Check if both found:
i = 0
,j = -1
→ No, skip distance calculation - Current state:
i = 0
,j = -1
,ans = infinity
Index 1: word = "quick"
- Found
word2
! Updatej = 1
- Check if both found:
i = 0
,j = 1
→ Yes! Calculate distance - Distance =
|0 - 1| = 1
- Update minimum:
ans = min(infinity, 1) = 1
- Current state:
i = 0
,j = 1
,ans = 1
Index 2: word = "brown"
- Not either target word, continue
- Current state:
i = 0
,j = 1
,ans = 1
Index 3: word = "fox"
- Not either target word, continue
- Current state:
i = 0
,j = 1
,ans = 1
Index 4: word = "quick"
- Found
word2
! Updatej = 4
- Check if both found:
i = 0
,j = 4
→ Yes! Calculate distance - Distance =
|0 - 4| = 4
- Update minimum:
ans = min(1, 4) = 1
- Current state:
i = 0
,j = 4
,ans = 1
Final Result: The minimum distance is 1
The key insight demonstrated here is that we only keep the most recent position of each word. When "quick" appears again at index 4, we update j
from 1 to 4. This doesn't improve our answer (distance becomes 4 instead of 1), but if "the" appeared later in the array, we'd want to compare with the most recent "quick" at index 4, not the older one at index 1.
Solution Implementation
1class Solution:
2 def shortestDistance(self, wordsDict: List[str], word1: str, word2: str) -> int:
3 # Initialize indices for tracking positions of word1 and word2
4 word1_index = -1
5 word2_index = -1
6
7 # Initialize minimum distance to infinity
8 min_distance = float('inf')
9
10 # Iterate through the words list with index
11 for current_index, current_word in enumerate(wordsDict):
12 # Update word1_index when we find word1
13 if current_word == word1:
14 word1_index = current_index
15
16 # Update word2_index when we find word2
17 if current_word == word2:
18 word2_index = current_index
19
20 # If both words have been found at least once, calculate distance
21 if word1_index != -1 and word2_index != -1:
22 min_distance = min(min_distance, abs(word1_index - word2_index))
23
24 return min_distance
25
1class Solution {
2 public int shortestDistance(String[] wordsDict, String word1, String word2) {
3 // Initialize minimum distance to a large value (infinity)
4 int minDistance = Integer.MAX_VALUE;
5
6 // Track the most recent positions of word1 and word2
7 int word1Position = -1;
8 int word2Position = -1;
9
10 // Iterate through the words dictionary
11 for (int currentIndex = 0; currentIndex < wordsDict.length; currentIndex++) {
12 // Update position if current word matches word1
13 if (wordsDict[currentIndex].equals(word1)) {
14 word1Position = currentIndex;
15 }
16
17 // Update position if current word matches word2
18 if (wordsDict[currentIndex].equals(word2)) {
19 word2Position = currentIndex;
20 }
21
22 // If both words have been found at least once, update minimum distance
23 if (word1Position != -1 && word2Position != -1) {
24 int currentDistance = Math.abs(word1Position - word2Position);
25 minDistance = Math.min(minDistance, currentDistance);
26 }
27 }
28
29 return minDistance;
30 }
31}
32
1class Solution {
2public:
3 int shortestDistance(vector<string>& wordsDict, string word1, string word2) {
4 // Initialize the minimum distance to maximum integer value
5 int minDistance = INT_MAX;
6
7 // Track the most recent positions of word1 and word2
8 // Initialize to -1 to indicate words haven't been found yet
9 int word1Position = -1;
10 int word2Position = -1;
11
12 // Iterate through the words dictionary
13 for (int currentIndex = 0; currentIndex < wordsDict.size(); ++currentIndex) {
14 // Update position when word1 is found
15 if (wordsDict[currentIndex] == word1) {
16 word1Position = currentIndex;
17 }
18
19 // Update position when word2 is found
20 if (wordsDict[currentIndex] == word2) {
21 word2Position = currentIndex;
22 }
23
24 // If both words have been found at least once, calculate distance
25 if (word1Position != -1 && word2Position != -1) {
26 // Update minimum distance between the two words
27 minDistance = min(minDistance, abs(word1Position - word2Position));
28 }
29 }
30
31 return minDistance;
32 }
33};
34
1function shortestDistance(wordsDict: string[], word1: string, word2: string): number {
2 // Initialize the minimum distance to maximum safe integer value
3 let minDistance: number = Number.MAX_SAFE_INTEGER;
4
5 // Track the most recent positions of word1 and word2
6 // Initialize to -1 to indicate words haven't been found yet
7 let word1Position: number = -1;
8 let word2Position: number = -1;
9
10 // Iterate through the words dictionary
11 for (let currentIndex = 0; currentIndex < wordsDict.length; currentIndex++) {
12 // Update position when word1 is found
13 if (wordsDict[currentIndex] === word1) {
14 word1Position = currentIndex;
15 }
16
17 // Update position when word2 is found
18 if (wordsDict[currentIndex] === word2) {
19 word2Position = currentIndex;
20 }
21
22 // If both words have been found at least once, calculate distance
23 if (word1Position !== -1 && word2Position !== -1) {
24 // Update minimum distance between the two words
25 minDistance = Math.min(minDistance, Math.abs(word1Position - word2Position));
26 }
27 }
28
29 return minDistance;
30}
31
Time and Space Complexity
Time Complexity: O(n)
where n
is the length of wordsDict
. The algorithm iterates through the list exactly once, performing constant-time operations (comparisons, assignments, and min calculation) at each step.
Space Complexity: O(1)
. The algorithm uses only a fixed amount of extra space for variables i
, j
, ans
, and k
, regardless of the input size. No additional data structures are created that scale with the input.
Common Pitfalls
Pitfall 1: Handling the Case When word1 and word2 Are the Same
A critical edge case that's often overlooked is when word1
and word2
are identical strings. The current implementation will always return 0 in this case because both word1_index
and word2_index
will be updated to the same value whenever the word is found, resulting in abs(word1_index - word2_index) = 0
.
However, the problem asks for the distance between two different occurrences of words. When the words are the same, we need to find the minimum distance between different instances of that word.
Incorrect behavior example:
wordsDict = ["a", "b", "a"]
,word1 = "a"
,word2 = "a"
- Current code returns 0 (incorrect)
- Expected output: 2 (distance between indices 0 and 2)
Solution to the Pitfall
We need to handle two separate cases: when the words are different and when they're the same.
class Solution:
def shortestDistance(self, wordsDict: List[str], word1: str, word2: str) -> int:
if word1 == word2:
# Special case: find minimum distance between same word occurrences
prev_index = -1
min_distance = float('inf')
for current_index, current_word in enumerate(wordsDict):
if current_word == word1:
if prev_index != -1:
min_distance = min(min_distance, current_index - prev_index)
prev_index = current_index
return min_distance
else:
# Original logic for different words
word1_index = -1
word2_index = -1
min_distance = float('inf')
for current_index, current_word in enumerate(wordsDict):
if current_word == word1:
word1_index = current_index
elif current_word == word2:
word2_index = current_index
if word1_index != -1 and word2_index != -1:
min_distance = min(min_distance, abs(word1_index - word2_index))
return min_distance
Alternative Unified Solution
A more elegant approach that handles both cases without branching:
class Solution:
def shortestDistance(self, wordsDict: List[str], word1: str, word2: str) -> int:
prev_index = -1
min_distance = float('inf')
for current_index, current_word in enumerate(wordsDict):
if current_word == word1 or current_word == word2:
# If prev_index exists and either:
# 1. Words are different, OR
# 2. Words are same but we found another occurrence
if prev_index != -1 and (word1 != word2 or wordsDict[prev_index] == current_word):
min_distance = min(min_distance, current_index - prev_index)
prev_index = current_index
return min_distance
This unified solution tracks only the previous relevant index and updates the minimum distance whenever appropriate, handling both same and different word cases seamlessly.
Which data structure is used to implement priority queue?
Recommended Readings
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!