[LeetCode]642. Design Search Autocomplete System(JavaScript解法)

题目描述

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 the sentences and times arrays.
  • List<String> input(char c) This indicates that the user typed the character c.
    • Returns an empty array [] if c == '#' 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 than 3 matches, return them all.

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 to input.

思路分析

搜索自动补全设计题。

在AutocompleteSystem类中,我们需要 1: 存储历史搜索记录和其对映热度的映射;2: 用户输入#号前所有输入内容的记录;3. 所有符合匹配和排序规则的候选句子优先级队列 这3个变量来完成自动补全的逻辑。可以用Map和Array来实现这些数据结构。

  • historySearch - 初始值来自构造函数的入参sentences,以句子为key,热度为value存入map中;当用户输入完一个完整句子(输入“#”后)时进行更新;
  • inputData - 初始值为[],调用AutocompleteSystem.input时更新;
  • queue - 初始值为[],储存<句子,热度>,始终保证热度高的句子排在队首;若有多个热度相同的句子,ASCII码小的排在前面。

数据结构定义好之后,再来看用户input操作时需要实现的逻辑:

  1. 若用户输入“#”,表明一个完整句子结束。此时:
    • 更新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  */
上一篇:Kubernetes(K8s)基本概念:HPA(Pod横向自动扩容)、StatefulSet


下一篇:一沙框架(YiShaAdmin) -修改数据后回到起始页的解决办法