RainX_69

Design a search query autocomplete system | hard | MUST DO

Mar 7th, 2023
103
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 11.51 KB | Source Code | 0 0
  1. https://practice.geeksforgeeks.org/problems/search-query-auto-complete/1?page=2&difficulty[]=1&difficulty[]=2&status[]=unsolved&category[]=Dynamic%20Programming&category[]=Binary%20Search&category[]=Trie&category[]=union-find&sortBy=submissions
  2.  
  3. Design a search query autocomplete system for a search engine.
  4.  
  5. The users will input a sentence ( which may have multiple words and ends with special character '#').
  6.  
  7. For each character they type except '#', you need to return the top 3 previously entered and most frequently queried sentences that have prefix the same as the part of sentence already typed.
  8.  
  9. Here are the specific rules:
  10.  
  11. The frequency for a sentence is defined as the number of times a user typed the exactly same sentence before.
  12. The returned top 3 sentences should be sorted by frequency (The first is the most frequent).  If several sentences have the same frequency, you need to use ASCII-code order (smaller one appears first).
  13. If less than 3 valid sentences exist, then just return as many as you can.
  14. When the input is a special character, it means the sentence ends, and in this case, you need to return an empty list.
  15.  
  16.  
  17. Your job is to implement the methods of the AutoCompleteSystem:
  18.  
  19. AutoCompleteSystem(String[] sentences, int[] times): This is the constructor. The input is previously used 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 sentences.
  20. Now, the user wants to input a new sentence. The following function will provide the next character the user types:
  21.  
  22. String[] input(char c): 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 an array will be the top 3 historical sentences that have prefix the same as the part of sentence already typed.
  23.  
  24.  
  25. Example:
  26. Operation:
  27. AutoCompleteSystem(["i love you", "island",
  28. "ironman", "i love geeksforgeeks"], [5,3,2,2])
  29. The system have already tracked down the
  30. following sentences and their corresponding
  31. times:
  32. "i love you" : 5 times
  33. "island" : 3 times
  34. "ironman" : 2 times
  35. "i love geeksforgeeks" : 2 times
  36. Now, the user begins another search:
  37. Operation: input('i')
  38. Output:
  39. ["i love you", "island","i love geeksforgeeks"]
  40. Explanation:
  41. There are four sentences that have prefix
  42. "i". Among them, "ironman" and "i love
  43. geeksforgeeks" have same frequency. Since
  44. ' ' has ASCII code 32 and 'r' has ASCII code
  45.  114, "i love geeksforgeeks" should be in
  46. front of "ironman". Also we only need to
  47. output top 3 most frequent sentences, so
  48. "ironman" will be ignored.
  49. Operation: input(' ')
  50. Output: ["i love you","i love geeksforgeeks"]
  51. Explanation:
  52. There are only two sentences that have prefix
  53. "i ".
  54. Operation: input('a')
  55. Output: []
  56. Explanation:
  57. There are no sentences that have prefix "i a"
  58. Operation: input('#')
  59. Output: []
  60. Explanation:
  61. The user finished the input, the sentence
  62. "i a" should be saved as a historical
  63. sentence in system. And the next input
  64. will be counted as a new search.
  65.  
  66. ---------------------------------------------------------------------------------------------------------------------------------------
  67.  
  68. BRUTE FORCE
  69.  
  70. struct TrieNode{
  71.     TrieNode* children[256]={nullptr};
  72.     bool isTerminal;
  73.     TrieNode(){
  74.         isTerminal=false;
  75.     }
  76. };
  77.  
  78. struct cmp{
  79.     bool operator()(pair<int,string> &P1, pair<int,string> &P2){
  80.         if(P1.first==P2.first){
  81.             string sentence1=P1.second;
  82.             string sentence2=P2.second;
  83.             int result=sentence1.compare(sentence2);
  84.             return result<0;
  85.             }
  86.         return P1.first>P2.first;
  87.     }
  88. };
  89.    
  90. class AutoCompleteSystem {
  91. private:
  92.     priority_queue<pair<int,string>,vector<pair<int,string>>,cmp> pq;
  93.     string inProgress="";
  94.     TrieNode* root;
  95.     unordered_map<string,int> count;
  96. public:
  97.     void insert(string &str){
  98.         TrieNode* pCrawl=root;
  99.         for(auto c: str){
  100.             if(pCrawl->children[c]==NULL){
  101.                 pCrawl->children[c]=new TrieNode();
  102.             }
  103.             pCrawl=pCrawl->children[c];
  104.         }
  105.         pCrawl->isTerminal=true;
  106.     }
  107.    
  108.     AutoCompleteSystem(vector<string>& sentences, vector<int>& times) {
  109.         root=new TrieNode();
  110.         for(int i=0;i<sentences.size();i++){
  111.             insert(sentences[i]);
  112.             count[sentences[i]]=times[i];
  113.         }
  114.     }
  115.    
  116.     bool isLeaf(TrieNode* node){
  117.         for(int i=0;i<256;i++){
  118.             if(node->children[i]!=nullptr){
  119.                 return false;
  120.             }
  121.         }
  122.         return true;
  123.     }
  124.    
  125.     void search(TrieNode* root, string &prefix, string builder, int curr){
  126.         if(curr==prefix.size()){
  127.             if(root->isTerminal==true){
  128.                 pq.push({count[builder],builder});
  129.                 if(pq.size()>3){
  130.                     pq.pop();
  131.                 }
  132.             }
  133.             if(isLeaf(root)==true){
  134.                 return;
  135.             }
  136.             for(int i=0;i<256;i++){
  137.                 if(root->children[i]!=nullptr){
  138.                     search(root->children[i],prefix,builder+char(i),curr);
  139.                 }
  140.             }
  141.             return;
  142.         }
  143.        
  144.        
  145.         if(root->children[prefix[curr]]!=nullptr){
  146.             search(root->children[prefix[curr]],prefix,builder+prefix[curr],curr+1);
  147.         }
  148.     }
  149.    
  150.     vector<string> input(char c) {
  151.         vector<string> res;
  152.         if(c=='#'){
  153.             insert(inProgress);
  154.             count[inProgress]++;
  155.             inProgress="";
  156.             return res;
  157.         }
  158.         inProgress+=c;
  159.         search(root,inProgress,"",0);
  160.         while(!pq.empty()){
  161.             res.push_back(pq.top().second);
  162.             pq.pop();
  163.         }
  164.         reverse(res.begin(),res.end());
  165.         return res;
  166.     }
  167. };
  168. ---------------------------------------------------------------------------------------------------------------------------------------
  169.  
  170. SEMI OPTIMAL APPROACH
  171.  
  172. Optimization is be done here. The idea is to keep storing the words as you go in the node. This way the search is limited to prefix only and you wouldn't need to search all words
  173.  
  174.  
  175. struct TrieNode{
  176.    TrieNode* children[256]={nullptr};
  177.    unordered_set<string> words;
  178.    TrieNode(){}
  179. };
  180.  
  181. struct cmp{
  182.    bool operator()(pair<int,string> &P1, pair<int,string> &P2){
  183.        if(P1.first==P2.first){
  184.            string sentence1=P1.second;
  185.            string sentence2=P2.second;
  186.            int result=sentence1.compare(sentence2);
  187.            return result<0;
  188.            }
  189.        return P1.first>P2.first;
  190.    }
  191. };
  192.    
  193. class AutoCompleteSystem {
  194. private:
  195.    TrieNode* root;
  196.    string inProgress="";
  197.    unordered_map<string,int> count;
  198.    priority_queue<pair<int,string>,vector<pair<int,string>>,cmp> pq;
  199. public:
  200.    AutoCompleteSystem(vector<string>& sentences, vector<int>& times) {
  201.        root=new TrieNode();
  202.        for(int i=0;i<sentences.size();i++){
  203.            insert(sentences[i]);
  204.            count[sentences[i]]=times[i];
  205.        }
  206.    }
  207.    
  208.    void insert(string &str){
  209.        TrieNode* pCrawl=root;
  210.        for(auto c: str){
  211.            if(pCrawl->children[c]==NULL){
  212.                pCrawl->children[c]=new TrieNode();
  213.            }
  214.            pCrawl=pCrawl->children[c];
  215.            (pCrawl->words).insert(str);
  216.        }
  217.        (pCrawl->words).insert(str);
  218.    }
  219.    
  220.    vector<string> input(char c) {
  221.        if(c=='#'){
  222.             insert(inProgress);
  223.             count[inProgress]++;
  224.             inProgress="";
  225.             return {};
  226.         }
  227.         vector<string> res;
  228.         inProgress+=c;
  229.         TrieNode* ptr=root;
  230.         int i=0;
  231.         while(i<inProgress.size() && ptr->children[inProgress[i]]!=NULL){
  232.             ptr=ptr->children[inProgress[i]];
  233.             i++;
  234.         }
  235.         if(i==inProgress.size()){
  236.             unordered_set<string> WORDS=ptr->words;
  237.             for(auto word: WORDS){
  238.                 pq.push({count[word],word});
  239.                 if(pq.size()>3){
  240.                     pq.pop();
  241.                 }
  242.             }
  243.             while(!pq.empty()){
  244.                 res.push_back(pq.top().second);
  245.                 pq.pop();
  246.             }
  247.             reverse(res.begin(),res.end());
  248.         }
  249.         return res;
  250.     }
  251. };
  252.  
  253. ---------------------------------------------------------------------------------------------------------------------------------------
  254.  
  255. OPTIMAL ANSWER
  256.  
  257. Can you optimise it even furthur??...Well, if you think, we are searching for this inProgress again and again for just one word addition. Let us say inProgress currently is "i hate you reader" and let us say, we add c to it, so it becomes "i hate you readerc", now that is not available. SO, it does not matter how many words get attached now, you will never ever get any answer.
  258.  
  259. Keep a tracker that tracks the current node we are right on. If you add a new letter and that is unavailable in tracker's children. Mark the tracker something so no matter what letters we attach, tracker will notify that no answer exist.. Else, just move the tracker forward :)
  260.  
  261.  
  262. struct TrieNode{
  263.    TrieNode* children[256]={nullptr};
  264.    unordered_set<string> words;
  265.    TrieNode(){}
  266. };
  267.  
  268. struct cmp{
  269.    bool operator()(pair<int,string> &P1, pair<int,string> &P2){
  270.        if(P1.first==P2.first){
  271.            string sentence1=P1.second;
  272.            string sentence2=P2.second;
  273.            int result=sentence1.compare(sentence2);
  274.            return result<0;
  275.        }
  276.        return P1.first>P2.first;
  277.    }
  278. };
  279.    
  280. class AutoCompleteSystem {
  281. private:
  282.    TrieNode* tracker;
  283.    TrieNode* root;
  284.    string inProgress="";
  285.    unordered_map<string,int> count;
  286.    priority_queue<pair<int,string>,vector<pair<int,string>>,cmp> pq;
  287. public:
  288.    AutoCompleteSystem(vector<string>& sentences, vector<int>& times) {
  289.        root=new TrieNode();
  290.        for(int i=0;i<sentences.size();i++){
  291.            insert(sentences[i],times[i]);
  292.        }
  293.        tracker=root;
  294.    }
  295.    
  296.    void insert(string &str, int freq){
  297.        if(count.find(str)!=count.end()){
  298.            count[str]+=freq;
  299.            return;
  300.        }
  301.        TrieNode* pCrawl=root;
  302.        for(auto c: str){
  303.            if(pCrawl->children[c]==NULL){
  304.                pCrawl->children[c]=new TrieNode();
  305.            }
  306.            pCrawl=pCrawl->children[c];
  307.            pCrawl->words.insert(str);
  308.        }
  309.        count[str]=freq;
  310.    }
  311.    
  312.    vector<string> input(char c) {
  313.        if(c=='#'){
  314.             insert(inProgress,1);
  315.             inProgress="";
  316.             tracker=root;
  317.             return {};
  318.         }
  319.         inProgress+=c;
  320.         vector<string> res;
  321.         if(tracker==nullptr){
  322.             return res;
  323.         }
  324.         if(tracker->children[c]!=nullptr){
  325.             tracker=tracker->children[c];
  326.             for(auto word: tracker->words){
  327.                 pq.push({count[word],word});
  328.                 if(pq.size()>3){
  329.                     pq.pop();
  330.                 }
  331.             }
  332.             while(!pq.empty()){
  333.                 res.push_back(pq.top().second);
  334.                 pq.pop();
  335.             }
  336.             reverse(res.begin(),res.end());
  337.         }
  338.         else{
  339.             tracker=nullptr; // because no matter what, any furthur additions to inProgress would not yeild any answer anyways
  340.         }
  341.         return res;
  342.     }
  343. };
Advertisement
Add Comment
Please, Sign In to add comment