题目描述
LeetCode原题链接:642. Design Search Autocomplete System
Design a search autocomplete system for a search engine. Users may input a sentence (at least one word and end with a special character '#'
).
You are given a string array sentences
and an integer array times
both of length n
where sentences[i]
is a previously typed sentence and times[i]
is the corresponding number of times the sentence was typed. For each input character except '#'
, return the top 3
historical hot sentences that have the same prefix as the part of the 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 hot degree, use ASCII-code order (smaller one appears first). - If less than
3
hot sentences exist, 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.
Implement the AutocompleteSystem
class:
-
AutocompleteSystem(String[] sentences, int[] times)
Initializes the object with thesentences
andtimes
arrays. -
List<String> input(char c)
This indicates that the user typed the characterc
.- Returns an empty array
[]
ifc == '#'
and stores the inputted sentence in the system. - Returns the top
3
historical hot sentences that have the same prefix as the part of the sentence already typed. If there are fewer than3
matches, return them all.
- Returns an empty array
Example 1:
Input ["AutocompleteSystem", "input", "input", "input", "input"] [[["i love you", "island", "iroman", "i love leetcode"], [5, 3, 2, 2]], ["i"], [" "], ["a"], ["#"]] Output [null, ["i love you", "island", "i love leetcode"], ["i love you", "i love leetcode"], [], []] Explanation AutocompleteSystem obj = new AutocompleteSystem(["i love you", "island", "iroman", "i love leetcode"], [5, 3, 2, 2]); obj.input("i"); // return ["i love you", "island", "i love leetcode"]. 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. obj.input(" "); // return ["i love you", "i love leetcode"]. There are only two sentences that have prefix "i ". obj.input("a"); // return []. There are no sentences that have prefix "i a". obj.input("#"); // return []. 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.
Constraints:
n == sentences.length
n == times.length
1 <= n <= 100
1 <= sentences[i].length <= 100
1 <= times[i] <= 50
-
c
is a lowercase English letter, a hash'#'
, or space' '
. - Each tested sentence will be a sequence of characters
c
that end with the character'#'
. - Each tested sentence will have a length in the range
[1, 200]
. - The words in each input sentence are separated by single spaces.
- At most
5000
calls will be made toinput
.
思路分析
搜索自动补全设计题。
在AutocompleteSystem类中,我们需要 1: 存储历史搜索记录和其对映热度的映射;2: 用户输入#号前所有输入内容的记录;3. 所有符合匹配和排序规则的候选句子优先级队列 这3个变量来完成自动补全的逻辑。可以用Map和Array来实现这些数据结构。
- historySearch - 初始值来自构造函数的入参sentences,以句子为key,热度为value存入map中;当用户输入完一个完整句子(输入“#”后)时进行更新;
- inputData - 初始值为[],调用AutocompleteSystem.input时更新;
- queue - 初始值为[],储存<句子,热度>,始终保证热度高的句子排在队首;若有多个热度相同的句子,ASCII码小的排在前面。
数据结构定义好之后,再来看用户input操作时需要实现的逻辑:
- 若用户输入“#”,表明一个完整句子结束。此时:
-
- 更新historySearch:inputData出现过的话就times+1,没有出现过就新加入到map中;
- 清空inputData和queue;
- 输出空数组[]。
2. 若用户输入非“#”号:
-
- 第一个输入的字符:在historySearch中搜索匹配输入字母的句子,用这些匹配的句子初始化优先级队列queue
- 非第一个输入的字符:根据新输入的字母,用新生成的inputData去移除queue不匹配的项
- 输出queue的前三项;若queue.length < 3,有几项输出几项。
这里需要再另外写两个工具类,首先是优先级队列的插入操作。因为js中没有priority_queue或是heap数据结构,所以这里用数组+二分法+插入排序来实现这个功能。先根据热度大小进行排序,找到从左到右数第一个 <= 要插入的句子的热度的数组下标index。如果index处的句子热度和要插入的句子热度相等,再比较ASCII码。
另一个是检查输入的字符串和句子是否匹配。可以用正则表达式+字符串test方法,也可以直接for循环一位一位比较,有一位不匹配就break返回false。(两种代码提交后发现for循环进行匹配runtime更短)
代码示例(Js)
这道题思路比较清晰,相对而言不是很复杂。不过网上没大看到js解法的solution,所以这里用js写一下代码示例:
1 /** 2 * @param {string[]} sentences 3 * @param {number[]} times 4 */ 5 var AutocompleteSystem = function(sentences, times) { 6 this.historySearch = new Map(); // 历史搜索内容&热度 映射表 7 sentences.forEach((sentence, index) => { 8 this.historySearch.set(sentence, times[index]); 9 }); 10 this.inputData = []; // 用户输入的字符串 11 this.queue = []; // 优先级队列 12 }; 13 14 /** 15 * @param {character} c 16 * @return {string[]} 17 */ 18 AutocompleteSystem.prototype.input = function(c) { 19 if(c == '#') { 20 // 更新搜索历史 21 let finalSentence = this.inputData.join(''); 22 let times = this.historySearch.has(finalSentence) ? this.historySearch.get(finalSentence) : 0; 23 this.historySearch.set(finalSentence, times + 1); 24 25 this.inputData = []; 26 this.queue = []; 27 return []; 28 } 29 if(this.inputData.length == 0) { 30 for(let [sentence, times] of this.historySearch) { 31 if(this.isMatch(sentence, c)) { 32 this.updateQueue([sentence, times]); 33 } 34 } 35 this.inputData.push(c); 36 } 37 else { 38 this.inputData.push(c); 39 this.queue = this.queue.filter((item) => this.isMatch(item[0], this.inputData.join(''))); 40 } 41 let result = []; 42 for(let i = 0; i < this.queue.length; i++) { 43 result.push(this.queue[i][0]); 44 if(result.length == 3) break; 45 } 46 return result; 47 }; 48 49 // 向优先级队列中插入新的元素(binary search + 插入排序) 50 AutocompleteSystem.prototype.updateQueue = function(pair) { 51 let left = 0, right = this.queue.length; 52 while(left < right) { 53 let middle = Math.floor((left + right) / 2); 54 // 用热度进行比较 55 if(this.queue[middle][1] > pair[1]) { 56 left = middle + 1; 57 } 58 else { 59 right = middle; 60 } 61 } 62 // 热度相同根据ASCII码进行比较 63 while(right < this.queue.length && this.queue[right][1] == pair[1]) { 64 if(pair[0] < this.queue[right][0]) break; 65 right++; 66 } 67 this.queue.splice(right, 0, pair); 68 }; 69 70 // 判断input是否和当前sentence匹配 71 AutocompleteSystem.prototype.isMatch = function(sentence, prefix) { 72 // let expression = new RegExp("^" + prefix); 73 // return expression.test(sentence); 74 for(let i = 0; i < prefix.length; i++) { 75 if(prefix[i] != sentence[i]) return false; 76 } 77 return true; 78 }; 79 80 /** 81 * Your AutocompleteSystem object will be instantiated and called as such: 82 * var obj = new AutocompleteSystem(sentences, times) 83 * var param_1 = obj.input(c) 84 */