|
| 1 | +--- |
| 2 | +id: shortest-distance-between-words-II |
| 3 | +title: Shortest Distance Between Words |
| 4 | +sidebar_label: 0244-Shortest Distance Between Words - II |
| 5 | +tags: [Hash Map, Two Pointers] |
| 6 | +description: Solution to finding the shortest distance between two words in an array of strings. |
| 7 | +--- |
| 8 | + |
| 9 | +## Problem Statement |
| 10 | + |
| 11 | +### Problem Description |
| 12 | + |
| 13 | +Design a data structure that can compute the shortest distance between any two distinct strings within an array of strings. Initialize the data structure with an array of strings, `wordsDict`. Once initialized, it should return the smallest possible index difference between two different strings in `wordsDict` when queried. |
| 14 | + |
| 15 | +### Examples |
| 16 | + |
| 17 | +**Example 1:** |
| 18 | + |
| 19 | +``` |
| 20 | +Input: wordsDict = ["practice", "makes", "perfect", "coding", "makes"] |
| 21 | +Query: word1 = "coding", word2 = "practice" |
| 22 | +Output: 3 |
| 23 | +``` |
| 24 | + |
| 25 | +**Example 2:** |
| 26 | + |
| 27 | +``` |
| 28 | +Input: wordsDict = ["practice", "makes", "perfect", "coding", "makes"] |
| 29 | +Query: word1 = "makes", word2 = "coding" |
| 30 | +Output: 1 |
| 31 | +``` |
| 32 | + |
| 33 | +### Constraints |
| 34 | + |
| 35 | +- The number of words in `wordsDict` is in the range [1, 10^5]. |
| 36 | +- `wordsDict[i]` consists of lowercase English letters. |
| 37 | +- `word1` and `word2` are distinct and will always be in `wordsDict`. |
| 38 | + |
| 39 | +## Solution of Given Problem |
| 40 | + |
| 41 | +### Intuition and Approach |
| 42 | + |
| 43 | +To efficiently find the shortest distance between two words in the dictionary, a preprocessing step is required during initialization. We traverse the `wordsDict` array once and create a hash map where keys are the distinct words from the array, and values are lists of indices where each key word is located in the original array. This preprocessing step allows for a quick lookup of the positions of any word, facilitating the computation of the distances between any two words. |
| 44 | + |
| 45 | +Once the positions are mapped, to find the shortest distance between `word1` and `word2`, we get their list of indices from our hash map. We need to find the minimum difference between any two indices from these lists. The lists are already sorted because the indices were appended in the order they were encountered during initialization. |
| 46 | + |
| 47 | +A two-pointer approach efficiently solves the problem of finding the minimum difference. Start with the first index of each list, and at each step, move the pointer that points to the smaller index to the next index in its list. This approach will traverse the two lists simultaneously and will always give the smallest possible difference in indices (thus the shortest distance) between `word1` and `word2`. The process repeats until we have fully traversed one of the lists, ensuring that no potential shorter distance is missed. |
| 48 | + |
| 49 | +### Approaches |
| 50 | + |
| 51 | +#### Codes in Different Languages |
| 52 | + |
| 53 | +<Tabs> |
| 54 | + <TabItem value="JavaScript" label="JavaScript" default> |
| 55 | + <SolutionAuthor name="@pallasivasai"/> |
| 56 | + ```javascript |
| 57 | + class WordDistance { |
| 58 | + constructor(wordsDict) { |
| 59 | + this.word_positions = {}; |
| 60 | + for (let index = 0; index < wordsDict.length; index++) { |
| 61 | + const word = wordsDict[index]; |
| 62 | + if (!this.word_positions[word]) { |
| 63 | + this.word_positions[word] = []; |
| 64 | + } |
| 65 | + this.word_positions[word].push(index); |
| 66 | + } |
| 67 | + } |
| 68 | + |
| 69 | + shortest(word1, word2) { |
| 70 | + const positions1 = this.word_positions[word1]; |
| 71 | + const positions2 = this.word_positions[word2]; |
| 72 | + let i = 0, j = 0, min_distance = Infinity; |
| 73 | + |
| 74 | + while (i < positions1.length && j < positions2.length) { |
| 75 | + const index1 = positions1[i], index2 = positions2[j]; |
| 76 | + min_distance = Math.min(min_distance, Math.abs(index1 - index2)); |
| 77 | + if (index1 < index2) { |
| 78 | + i++; |
| 79 | + } else { |
| 80 | + j++; |
| 81 | + } |
| 82 | + } |
| 83 | + |
| 84 | + return min_distance; |
| 85 | + } |
| 86 | + } |
| 87 | + |
| 88 | + // Example Usage |
| 89 | + const wordsDict = ["practice", "makes", "perfect", "coding", "makes"]; |
| 90 | + const wordDistance = new WordDistance(wordsDict); |
| 91 | + console.log(wordDistance.shortest("coding", "practice")); // Output: 3 |
| 92 | + console.log(wordDistance.shortest("makes", "coding")); // Output: 1 |
| 93 | + ``` |
| 94 | + |
| 95 | + </TabItem> |
| 96 | + <TabItem value="TypeScript" label="TypeScript"> |
| 97 | + <SolutionAuthor name="@pallasivasai"/> |
| 98 | + ```typescript |
| 99 | + class WordDistance { |
| 100 | + private word_positions: { [key: string]: number[] } = {}; |
| 101 | +
|
| 102 | + constructor(wordsDict: string[]) { |
| 103 | + for (let index = 0; index < wordsDict.length; index++) { |
| 104 | + const word = wordsDict[index]; |
| 105 | + if (!this.word_positions[word]) { |
| 106 | + this.word_positions[word] = []; |
| 107 | + } |
| 108 | + this.word_positions[word].push(index); |
| 109 | + } |
| 110 | + } |
| 111 | +
|
| 112 | + shortest(word1: string, word2: string): number { |
| 113 | + const positions1 = this.word_positions[word1]; |
| 114 | + const positions2 = this.word_positions[word2]; |
| 115 | + let i = 0, j = 0, min_distance = Infinity; |
| 116 | +
|
| 117 | + while (i < positions1.length && j < positions2.length) { |
| 118 | + const index1 = positions1[i], index2 = positions2[j]; |
| 119 | + min_distance = Math.min(min_distance, Math.abs(index1 - index2)); |
| 120 | + if (index1 < index2) { |
| 121 | + i++; |
| 122 | + } else { |
| 123 | + j++; |
| 124 | + } |
| 125 | + } |
| 126 | +
|
| 127 | + return min_distance; |
| 128 | + } |
| 129 | + } |
| 130 | +
|
| 131 | + // Example Usage |
| 132 | + const wordsDict = ["practice", "makes", "perfect", "coding", "makes"]; |
| 133 | + const wordDistance = new WordDistance(wordsDict); |
| 134 | + console.log(wordDistance.shortest("coding", "practice")); // Output: 3 |
| 135 | + console.log(wordDistance.shortest("makes", "coding")); // Output: 1 |
| 136 | + ``` |
| 137 | + |
| 138 | + </TabItem> |
| 139 | + <TabItem value="Python" label="Python"> |
| 140 | + <SolutionAuthor name="@pallasivasai"/> |
| 141 | + ```python |
| 142 | + class WordDistance: |
| 143 | + def __init__(self, wordsDict: list[str]): |
| 144 | + self.word_positions = {} |
| 145 | + for index, word in enumerate(wordsDict): |
| 146 | + if word not in self.word_positions: |
| 147 | + self.word_positions[word] = [] |
| 148 | + self.word_positions[word].append(index) |
| 149 | +
|
| 150 | + def shortest(self, word1: str, word2: str) -> int: |
| 151 | + positions1 = self.word_positions[word1] |
| 152 | + positions2 = self.word_positions[word2] |
| 153 | + i, j = 0, 0 |
| 154 | + min_distance = float('inf') |
| 155 | +
|
| 156 | + while i < len(positions1) and j < len(positions2): |
| 157 | + index1, index2 = positions1[i], positions2[j] |
| 158 | + min_distance = min(min_distance, abs(index1 - index2)) |
| 159 | + if index1 < index2: |
| 160 | + i += 1 |
| 161 | + else: |
| 162 | + j += 1 |
| 163 | +
|
| 164 | + return min_distance |
| 165 | +
|
| 166 | + # Example Usage |
| 167 | + wordsDict = ["practice", "makes", "perfect", "coding", "makes"] |
| 168 | + word_distance = WordDistance(wordsDict) |
| 169 | + print(word_distance.shortest("coding", "practice")) # Output: 3 |
| 170 | + print(word_distance.shortest("makes", "coding")) # Output: 1 |
| 171 | + ``` |
| 172 | + |
| 173 | + </TabItem> |
| 174 | + <TabItem value="Java" label="Java"> |
| 175 | + <SolutionAuthor name="@pallasivasai"/> |
| 176 | + ```java |
| 177 | + import java.util.*; |
| 178 | +
|
| 179 | + class WordDistance { |
| 180 | + private Map<String, List<Integer>> wordPositions; |
| 181 | +
|
| 182 | + public WordDistance(String[] wordsDict) { |
| 183 | + wordPositions = new HashMap<>(); |
| 184 | + for (int index = 0; index < wordsDict.length; index++) { |
| 185 | + String word = wordsDict[index]; |
| 186 | + if (!wordPositions.containsKey(word)) { |
| 187 | + wordPositions.put(word, new ArrayList<>()); |
| 188 | + } |
| 189 | + wordPositions.get(word).add(index); |
| 190 | + } |
| 191 | + } |
| 192 | +
|
| 193 | + public int shortest(String word1, String word2) { |
| 194 | + List<Integer> positions1 = wordPositions.get(word1); |
| 195 | + List<Integer> positions2 = wordPositions.get(word2); |
| 196 | + int i = 0, j = 0, minDistance = Integer.MAX_VALUE; |
| 197 | +
|
| 198 | + while (i < positions1.size() && j < positions2.size()) { |
| 199 | + int index1 = positions1.get(i), index2 = positions2.get(j); |
| 200 | + minDistance = Math.min(minDistance, Math.abs(index1 - index2)); |
| 201 | + if (index1 < index2) { |
| 202 | + i++; |
| 203 | + } else { |
| 204 | + j++; |
| 205 | + } |
| 206 | + } |
| 207 | +
|
| 208 | + return minDistance; |
| 209 | + } |
| 210 | +
|
| 211 | + public static void main(String[] args) { |
| 212 | + String[] wordsDict = {"practice", "makes", "perfect", "coding", "makes"}; |
| 213 | + WordDistance wordDistance = new WordDistance(wordsDict); |
| 214 | + System.out.println(wordDistance.shortest("coding", "practice")); // Output: 3 |
| 215 | + System.out.println(wordDistance.shortest("makes", "coding")); // Output: 1 |
| 216 | + } |
| 217 | + } |
| 218 | + ``` |
| 219 | + </TabItem> |
| 220 | + <TabItem value="cpp" label="C++"> |
| 221 | + <SolutionAuthor name="@sivaprasath2004"/> |
| 222 | + ```cpp |
| 223 | + #include <iostream> |
| 224 | + #include <vector> |
| 225 | + #include <unordered_map> |
| 226 | + #include <string> |
| 227 | + #include <climits> |
| 228 | + #include <algorithm> |
| 229 | +
|
| 230 | + class WordDistance { |
| 231 | + public: |
| 232 | + WordDistance(std::vector<std::string>& wordsDict) { |
| 233 | + for (int i = 0; i < wordsDict.size(); ++i) { |
| 234 | + word_positions[wordsDict[i]].push_back(i); |
| 235 | + } |
| 236 | + } |
| 237 | +
|
| 238 | + int shortest(const std::string& word1, const std::string& word2) { |
| 239 | + const std::vector<int>& positions1 = word_positions[word1]; |
| 240 | + const std::vector<int>& positions2 = word_positions[word2]; |
| 241 | + int i = 0, j = 0; |
| 242 | + int min_distance = INT_MAX; |
| 243 | +
|
| 244 | + while (i < positions1.size() && j < positions2.size()) { |
| 245 | + min_distance = std::min(min_distance, std::abs(positions1[i] - positions2[j])); |
| 246 | + if (positions1[i] < positions2[j]) { |
| 247 | + ++i; |
| 248 | + } else { |
| 249 | + ++j; |
| 250 | + } |
| 251 | + } |
| 252 | +
|
| 253 | + return min_distance; |
| 254 | + } |
| 255 | +
|
| 256 | + private: |
| 257 | + std::unordered_map<std::string, std::vector<int>> word_positions; |
| 258 | + }; |
| 259 | +
|
| 260 | + int main() { |
| 261 | + std::vector<std::string> wordsDict = {"practice", "makes", "perfect", "coding", "makes"}; |
| 262 | + WordDistance wordDistance(wordsDict); |
| 263 | + |
| 264 | + std::cout << wordDistance.shortest("coding", "practice") << std::endl; // Output: 3 |
| 265 | + std::cout << wordDistance.shortest("makes", "coding") << std::endl; // Output: 1 |
| 266 | +
|
| 267 | + return 0; |
| 268 | + } |
| 269 | + ``` |
| 270 | + </TabItem> |
| 271 | +</Tabs> |
| 272 | + |
| 273 | + |
| 274 | +### Explanation |
| 275 | + |
| 276 | +<Tabs> |
| 277 | +<TabItem value="JavaScript" label="javascript"> |
| 278 | + |
| 279 | +1. **Initialization (Constructor)**: |
| 280 | + - We iterate through the `wordsDict` array and populate the `wordPositions` map with arrays of indices for each word. This allows for quick lookups of the positions of any word in the list. |
| 281 | + |
| 282 | +2. **Shortest Distance (`shortest` method)**: |
| 283 | + - Retrieve the arrays of indices for `word1` and `word2` from the map. |
| 284 | + - Use two pointers (`i` and `j`) to traverse the arrays and compute the minimum distance. This two-pointer technique efficiently finds the minimum index difference by advancing the pointer that points to the smaller index. |
| 285 | + |
| 286 | +</TabItem> |
| 287 | +<TabItem value="TypeScript" label="typescript"> |
| 288 | + |
| 289 | +1. **Initialization (Constructor)**: |
| 290 | + - We iterate through the `wordsDict` array and populate the `wordPositions` map with arrays of indices for each word. This allows for quick lookups of the positions of any word in the list. |
| 291 | + |
| 292 | +2. **Shortest Distance (`shortest` method)**: |
| 293 | + - Retrieve the arrays of indices for `word1` and `word2` from the map. |
| 294 | + - Use two pointers (`i` and `j`) to traverse the arrays and compute the minimum distance. This two-pointer technique efficiently finds the minimum index difference by advancing the pointer that points to the smaller index. |
| 295 | + |
| 296 | + |
| 297 | +</TabItem> |
| 298 | +<TabItem value="Python" label="python"> |
| 299 | + |
| 300 | +1. **Initialization (`__init__` method)**: |
| 301 | + - We iterate through `wordsDict` and populate the `word_positions` dictionary with lists of indices for each word. |
| 302 | + |
| 303 | +2. **Shortest Distance (`shortest` method)**: |
| 304 | + - Retrieve the lists of indices for `word1` and `word2`. |
| 305 | + - Use two pointers (`i` and `j`) to traverse the lists and compute the minimum distance. This two-pointer technique ensures that we efficiently find the minimum index difference by advancing the pointer that points to the smaller index. |
| 306 | + |
| 307 | + |
| 308 | +</TabItem> |
| 309 | +<TabItem value="Java" label="java"> |
| 310 | + |
| 311 | +- **Initialization**: |
| 312 | + - `wordIndices` is a `Map` where each key is a word and each value is a list of indices where the word occurs in `wordsDict`. |
| 313 | + |
| 314 | +- **shortest Method**: |
| 315 | + - Uses two lists of indices for the given words and applies a two-pointer approach to find the smallest index difference. |
| 316 | + - The pointers traverse through the lists, adjusting to find the minimum distance efficiently. |
| 317 | + |
| 318 | +</TabItem> |
| 319 | +<TabItem value="cpp" label="C++"> |
| 320 | + |
| 321 | +1. **Initialization (Constructor)**: |
| 322 | + - We iterate through the `wordsDict` and populate the `word_positions` unordered map with vectors of indices for each word. |
| 323 | + |
| 324 | +2. **Shortest Distance (`shortest` method)**: |
| 325 | + - Retrieve the vectors of indices for `word1` and `word2` from the map. |
| 326 | + - Use two pointers (`i` and `j`) to traverse the vectors and compute the minimum distance. This two-pointer technique efficiently finds the minimum index difference by advancing the pointer that points to the smaller index. |
| 327 | +</TabItem> |
| 328 | + </Tabs> |
| 329 | + |
| 330 | +### Complexity |
| 331 | +<Tabs> |
| 332 | +<TabItem value="JavaScript" label="javascript"> |
| 333 | + |
| 334 | +- **Initialization**: O(n), where `n` is the number of words in `wordsDict`. |
| 335 | +- **Querying**: O(m + k), where `m` and `k` are the lengths of the arrays of indices for `word1` and `word2` respectively. This ensures that the queries are processed efficiently after the initial setup. |
| 336 | + |
| 337 | +</TabItem> |
| 338 | +<TabItem value="TypeScript" label="typescript"> |
| 339 | + |
| 340 | +- **Initialization**: O(n), where `n` is the number of words in `wordsDict`. |
| 341 | +- **Querying**: O(m + k), where `m` and `k` are the lengths of the arrays of indices for `word1` and `word2` respectively. This ensures that the queries are processed efficiently after the initial setup. |
| 342 | + |
| 343 | +</TabItem> |
| 344 | +<TabItem value="Python" label="python"> |
| 345 | + |
| 346 | +- **Initialization**: O(n), where `n` is the number of words in `wordsDict`. |
| 347 | +- **Querying**: O(m + k), where `m` and `k` are the lengths of the lists of indices for `word1` and `word2` respectively. This is efficient for typical use cases and allows quick queries after the initial setup. |
| 348 | + |
| 349 | +</TabItem> |
| 350 | +<TabItem value="Java" label="java"> |
| 351 | + |
| 352 | +- **Data Structure Design**: Use a Map `<String, List<Integer>>` to store the indices of each word in the `wordsDict`. This allows us to quickly access the positions of any word in the array. |
| 353 | + |
| 354 | +- **Initialization**: Traverse the `wordsDict` and populate the map with the indices of each word. |
| 355 | + |
| 356 | +- **Query Processing**: For each query, use the map to get the list of indices for the two words.Use a two-pointer technique to find the minimum distance between the indices of the two words. |
| 357 | + |
| 358 | +</TabItem> |
| 359 | +<TabItem value="cpp" label="C++"> |
| 360 | + |
| 361 | +- **Initialization**: O(n), where `n` is the number of words in `wordsDict`. |
| 362 | +- **Querying**: O(m + k), where `m` and `k` are the lengths of the vectors of indices for `word1` and `word2` respectively. This ensures that the queries are processed efficiently after the initial setup. |
| 363 | +</TabItem> |
| 364 | +</Tabs> |
| 365 | + |
| 366 | +## References |
| 367 | + |
| 368 | +- **LeetCode Problem:** [Shortest Word Distance-II](https://leetcode.com/problems/shortest-word-distance-ii/) |
| 369 | + |
| 370 | +<h2>Authors:</h2> |
| 371 | + |
| 372 | +<div style={{display: 'flex', flexWrap: 'wrap', justifyContent: 'space-between', gap: '10px'}}> |
| 373 | +{['sivaprasath2004','pallasivasai'].map(username => ( |
| 374 | + <Author key={username} username={username} /> |
| 375 | +))} |
| 376 | +</div> |
| 377 | + |
| 378 | + |
| 379 | + |
| 380 | + |
| 381 | + |
0 commit comments