package com.freetymekiyan.algorithms.level.medium; import org.junit.Assert; import org.junit.Test; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * This is a follow up of Shortest Word Distance. The only difference is now you are given the list of words and your * method will be called repeatedly many times with different parameters. How would you optimize it? * <p> * Design a class which receives a list of words in the constructor, and implements a method that takes two words word1 * and word2 and return the shortest distance between these two words in the list. * <p> * For example, * Assume that words = ["practice", "makes", "perfect", "coding", "makes"]. * <p> * Given word1 = “coding”, word2 = “practice”, return 3. * Given word1 = "makes", word2 = "coding", return 1. * <p> * Note: * You may assume that word1 does not equal to word2, and word1 and word2 are both in the list. * <p> * Company Tags: LinkedIn * Tags: Hash Table, Design * Similar Problems: (E) Merge Two Sorted Lists, (E) Shortest Word Distance, (M) Shortest Word Distance III */ public class ShortestWordDistance2 { @Test public void testExamples() { WordDistance w = new WordDistance(new String[]{"a", "b"}); Assert.assertEquals(1, w.shortest("a", "b")); Assert.assertEquals(1, w.shortest("b", "a")); } /** * Hash Table. * Store the word to all its indices mapping. * Get the shortest distance from two lists. */ public class WordDistance { Map<String, List<Integer>> locs; public WordDistance(String[] words) { locs = new HashMap<>(); for (int i = 0; i < words.length; i++) { if (!locs.containsKey(words[i])) { locs.put(words[i], new ArrayList<>()); } locs.get(words[i]).add(i); } } /** * Merge. O(m + n). * The indices are already sorted in the list. * To get shortest distance, just move the pointer with smaller value. * For i < indices1.size(), j < indices2.size(): * | Get indices in two lists, index1 and index2. * | If index1 > index2: * | Update shortest with min(shortest, index1 - index2). * | j++. * | Else * | Update shortest with min(shortest, index2 - index1). * | i++. */ public int shortest(String word1, String word2) { List<Integer> indices1 = locs.get(word1); List<Integer> indices2 = locs.get(word2); int shortest = Integer.MAX_VALUE; for (int i = 0, j = 0; i < indices1.size() && j < indices2.size(); ) { int index1 = indices1.get(i); int index2 = indices2.get(j); if (index1 > index2) { shortest = Math.min(shortest, index1 - index2); j++; } else { shortest = Math.min(shortest, index2 - index1); i++; } } return shortest; } } // Your WordDistance object will be instantiated and called as such: // WordDistance wordDistance = new WordDistance(words); // wordDistance.shortest("word1", "word2"); // wordDistance.shortest("anotherWord1", "anotherWord2"); }