[LeetCode 745] Prefix and Suffix Search
Given many words
, words[i]
has weight i
.
Design a class WordFilter
that supports one function, WordFilter.f(String prefix, String suffix)
. It will return the word with given prefix
and suffix
with maximum weight. If no word exists, return -1.
Examples:
Input:
WordFilter(["apple"])
WordFilter.f("a", "e") // returns 0
WordFilter.f("b", "") // returns -1
Note:
words
has length in range[1, 15000]
.- For each test case, up to
words.length
queriesWordFilter.f
may be made. words[i]
has length in range[1, 10]
.prefix, suffix
have lengths in range[0, 10]
.words[i]
andprefix, suffix
queries consist of lowercase letters only.
A natrual way of approaching this problem to store all words into a prefix trie and suffix trie separately, then try to find the intersection of all prefix-matched and suffix-matched words. But finding such an intersection can still takes up to O(N) time, N is the total number of words.
Since each word is up to 10 letters, so a much better solution is to insert each word's all possible suffix + a non-letter dividing character + the entire word. This way each time we do a query, we can simply concatenate the suffix with divider character + prefix to get a new prefix word and perform a prefix search in a prefix trie.
'{' is right after 'z', so we can pick it as the divider to make our implementation easier.
To insert all words plus their variations into a prefix trie, it takes O(N * K^2) time, N is the total number of words, K is the average length of each word. Each query takes O(K) time.
The space is O(N * K^2). This is the worst case, the more shared prefix and suffix we have, the less space it consumes.
class WordFilter { private class TrieNode { char c; TrieNode[] child; int weight; TrieNode(char c) { this.c = c; this.child = new TrieNode[27]; this.weight = -1; } } private class Trie { TrieNode root; Trie() { this.root = new TrieNode('#'); } void insert(String word, int w) { TrieNode curr = root; for(int i = 0; i < word.length(); i++) { int d = word.charAt(i) - 'a'; if(curr.child[d] == null) { curr.child[d] = new TrieNode(word.charAt(i)); } curr.child[d].weight = w; curr = curr.child[d]; } } int getMaxWeight(String prefix) { TrieNode curr = root; int maxWeight = -1; for(int i = 0; i < prefix.length(); i++) { int d = prefix.charAt(i) - 'a'; if(curr.child[d] == null) { return -1; } curr = curr.child[d]; maxWeight = curr.weight; } return maxWeight; } } private Trie trie; public WordFilter(String[] words) { trie = new Trie(); for(int i = 0; i < words.length; i++) { for(int j = 0; j <= words[i].length(); j++) { trie.insert(words[i].substring(j) + "{" + words[i], i); } } } public int f(String prefix, String suffix) { return trie.getMaxWeight(suffix + "{" + prefix); } } /** * Your WordFilter object will be instantiated and called as such: * WordFilter obj = new WordFilter(words); * int param_1 = obj.f(prefix,suffix); */