题目:
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?
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.
For example,
Assume that words = ["practice", "makes", "perfect", "coding", "makes"]
.
Given word1 = “coding”
, word2 = “practice”
, return 3.
Given word1 = "makes"
, word2 = "coding"
, return 1.
Note:
You may assume that word1 does not equal to word2, and word1 and word2 are both in the list.
链接: http://leetcode.com/problems/shortest-word-distance-ii/
题解:
这道题是上题的follow up, 假如要多次调用应该如何优化。 我们可以维护一个HashMap<String, ArrayList<Integer>>, map里面存储每个word以及出现的坐标index。这样查询的时候我们只需要get这两个单词的list,然后进行比较就可以了。比较的时候, 因为两个list都是排序后的, 所以我们可以用类似merge two sorted list的方法来计算minDistance。
Time Complexity - O(n), Space Complexity - O(n)
public class WordDistance { Map<String, ArrayList<Integer>> map; public WordDistance(String[] words) { this.map = new HashMap<>(); for(int i = 0; i < words.length; i++) { if(map.containsKey(words[i])) map.get(words[i]).add(i); else map.put(words[i], new ArrayList<Integer>(Arrays.asList(i))); } } public int shortest(String word1, String word2) { if(word1 == null || word2 == null) return Integer.MAX_VALUE; List<Integer> word1s = map.get(word1); List<Integer> word2s = map.get(word2); int minDistance = Integer.MAX_VALUE; int i = 0, j = 0; while(i < word1s.size() && j < word2s.size()) { minDistance = Math.min(minDistance, Math.abs(word1s.get(i) - word2s.get(j))); if(word1s.get(i) < word2s.get(j)) i++; else j++; } return minDistance; } } // Your WordDistance object will be instantiated and called as such: // WordDistance wordDistance = new WordDistance(words); // wordDistance.shortest("word1", "word2"); // wordDistance.shortest("anotherWord1", "anotherWord2");
二刷:
还是使用了一刷的方法。主要使用一个HashMap来把每个单词出现的index保存下来,这样就避免了每次都要完整遍历整个数组。要注意取得了两个单词出现index的list之后如何操作,就是使用一个O(n)的比较来一次性遍历两个list。
之前还考虑过使用Map<String, Map<String, Integer>>来保存之前出现过的结果,但这种方法只有重复查询较多时才会更有效。
Java:
单次查找, Time Complexity - O(n), Space Complexity - O(n)
public class WordDistance { Map<String, List<Integer>> map; public WordDistance(String[] words) { map = new HashMap<>(); for (int i = 0; i < words.length; i++) { String word = words[i]; if (!map.containsKey(word)) map.put(word, new ArrayList<>()); map.get(word).add(i); } } public int shortest(String word1, String word2) { List<Integer> l1 = map.get(word1); List<Integer> l2 = map.get(word2); int i = 0, j = 0; int minDist = Integer.MAX_VALUE; while (i < l1.size() && j < l2.size()) { int idx1 = l1.get(i); int idx2 = l2.get(j); if (idx1 < idx2) { minDist = Math.min(minDist, idx2 - idx1); i++; } else { minDist = Math.min(minDist, idx1 - idx2); j++; } } return minDist; } } // Your WordDistance object will be instantiated and called as such: // WordDistance wordDistance = new WordDistance(words); // wordDistance.shortest("word1", "word2"); // wordDistance.shortest("anotherWord1", "anotherWord2");
Reference:
https://leetcode.com/discuss/51698/9-line-o-n-c-solution
https://leetcode.com/discuss/50190/java-solution-using-hashmap