Description
Design a search autocomplete system for a search engine. Users may input a sentence (at least one word and end with a special character '#'
). For each character they type except '#', you need to return the top 3 historical hot sentences that have prefix the same as the part of sentence already typed. Here are the specific rules:
- The hot degree for a sentence is defined as the number of times a user typed the exactly same sentence before.
- The returned top 3 hot sentences should be sorted by hot degree (The first is the hottest one). If several sentences have the same degree of hot, you need to use ASCII-code order (smaller one appears first).
- If less than 3 hot sentences exist, then just return as many as you can.
- When the input is a special character, it means the sentence ends, and in this case, you need to return an empty list.
Your job is to implement the following functions:
The constructor function:
AutocompleteSystem(String[] sentences, int[] times):
This is the constructor. The input is historical data. Sentences
is a string array consists of previously typed sentences. Times
is the corresponding times a sentence has been typed. Your system should record these historical data.
Now, the user wants to input a new sentence. The following function will provide the next character the user types:
List
The input c
is the next character typed by the user. The character will only be lower-case letters ('a'
to 'z'
), blank space (' '
) or a special character ('#'
). Also, the previously typed sentence should be recorded in your system. The output will be the top 3 historical hot sentences that have prefix the same as the part of sentence already typed.
Example:
Operation: AutocompleteSystem(["i love you", "island","ironman", "i love leetcode"], [5,3,2,2])
The system have already tracked down the following sentences and their corresponding times:
"i love you"
: 5
times
"island"
: 3
times
"ironman"
: 2
times
"i love leetcode"
: 2
times
Now, the user begins another search:
Operation: input('i')
Output: ["i love you", "island","i love leetcode"]
Explanation:
There are four sentences that have prefix "i"
. Among them, "ironman" and "i love leetcode" have same hot degree. Since ' '
has ASCII code 32 and 'r'
has ASCII code 114, "i love leetcode" should be in front of "ironman". Also we only need to output top 3 hot sentences, so "ironman" will be ignored.
Operation: input(' ')
Output: ["i love you","i love leetcode"]
Explanation:
There are only two sentences that have prefix "i "
.
Operation: input('a')
Output: []
Explanation:
There are no sentences that have prefix "i a"
.
Operation: input('#')
Output: []
Explanation:
The user finished the input, the sentence "i a"
should be saved as a historical sentence in system. And the following input will be counted as a new search.
Note:
- The input sentence will always start with a letter and end with '#', and only one blank space will exist between two words.
- The number of complete sentences that to be searched won't exceed 100. The length of each sentence including those in the historical data won't exceed 100.
- Please use double-quote instead of single-quote when you write test cases even for a character input.
- Please remember to RESET your class variables declared in class AutocompleteSystem, as static/class variables are persisted across multiple test cases. Please see here for more details.
Solution
Trie, initiate time O(nl), input time O(k log k)
n: total sentences count
l: max sentence length
k: indicating the options available for the hot sentences
题目很长,但不太难,用Trie即可解决,不过需要注意:
- 在input中,需要边读边写,但注意一定是先读再写,否则会查出刚插进去的sentence
- 遇到'#'时,要返回empty list
- 由于要返回Trie中存储的sentences,一种做法是给Trie添加一个成员变量str用来存储root到curr路径上形成的str,另外一种做法是在查询时将str作为参数传入child。本题目采用的是后面的思路。
- 在dfs trie时,一定要现将root添加到结果集,再遍历children!否则会漏掉栈底的root。这点很重要,Trie的查询都要这么写。
- 在getKHot时,可以用PriorityQueue来做,也可以用List来做然后排序。
class AutocompleteSystem {
private Trie root;
private Trie curr;
private String str; // store currently visiting str
public AutocompleteSystem(String[] sentences, int[] times) {
root = new Trie();
for (int i = 0; i < sentences.length; ++i) {
insert(root, sentences[i], times[i]);
}
this.curr = root;
this.str = "";
}
public List input(char c) {
if (c == '#') {
insert(root, str, 1);
curr = root;
str = "";
return Collections.EMPTY_LIST; // return empty as designed
}
int i = getIndex(c);
if (curr.children[i] == null) {
curr.children[i] = new Trie();
}
str += c;
curr = curr.children[i];
return getKHot(curr, str, 3);
}
private void insert(Trie root, String s, int plusTimes) {
for (int i = 0; i < s.length(); ++i) {
int j = getIndex(s.charAt(i));
if (root.children[j] == null) {
root.children[j] = new Trie();
}
root = root.children[j];
}
root.times += plusTimes; // accumulate in case duplicate sentences
}
private List getKHot(Trie root, String s, int k) {
List list = new ArrayList<>();
dfs(root, s, list);
Collections.sort(list, (a, b)
-> (b.times != a.times
? b.times - a.times : a.str.compareTo(b.str)));
List res = new ArrayList<>();
for (int i = 0; i < Math.min(k, list.size()); ++i) {
res.add(list.get(i).str);
}
return res;
}
private void dfs(Trie root, String s, List list) {
if (root.times > 0) { // add root first
list.add(new Pair(s, root.times));
}
for (char c = 'a'; c <= 'z'; ++c) {
int i = getIndex(c);
if (root.children[i] != null) {
dfs(root.children[i], s + c, list);
}
}
if (root.children[26] != null) {
dfs(root.children[26], s + ' ', list);
}
}
private int getIndex(char c) {
return c == ' ' ? 26 : c - 'a';
}
class Pair {
String str;
int times;
public Pair(String s, int t) {
str = s;
times = t;
}
}
class Trie {
Trie[] children;
int times;
public Trie() {
children = new Trie[27];
}
}
}
/**
* Your AutocompleteSystem object will be instantiated and called as such:
* AutocompleteSystem obj = new AutocompleteSystem(sentences, times);
* List param_1 = obj.input(c);
*/