Advertisement
chevengur

СПРИНТ № 6 | Просто о сложности. Теория быстродействия | Урок 12: Дедупликатор документов

Apr 10th, 2024
724
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 21.51 KB | None | 0 0
  1. document.h
  2.  
  3. #pragma once
  4.  
  5. enum class DocumentStatus {
  6.     ACTUAL,
  7.     IRRELEVANT,
  8.     BANNED,
  9.     REMOVED,
  10. };
  11.  
  12. struct Document {
  13.     Document() = default;
  14.  
  15.     Document(int id, double relevance, int rating);
  16.  
  17.     int id = 0;
  18.     double relevance = 0.0;
  19.     int rating = 0;
  20. };
  21.  
  22. ***************************************************************************************************************************************
  23.  
  24. paginator.h
  25.  
  26. #pragma once
  27. #include <iostream>
  28.  
  29. template <typename Iterator>
  30. struct IteratorRange {
  31.     Iterator begin;
  32.     Iterator end;
  33.     IteratorRange(Iterator begin, Iterator end) :begin(begin), end(end) {}
  34. };
  35.  
  36. template <typename Iterator>
  37. class Paginator {
  38. public:
  39.  
  40.     Paginator(Iterator begin, Iterator end, int size)
  41.         :page_size_(size) {
  42.         vector test(begin, end);
  43.         Iterator temp = begin;
  44.         for (; temp + size < end; temp += size) {
  45.             pages_.push_back(IteratorRange(temp, temp + size));
  46.         }
  47.         if (temp < end) {
  48.             pages_.push_back(IteratorRange(temp, end));
  49.         }
  50.     }
  51.  
  52.     auto begin() const {
  53.         return pages_.begin();
  54.     }
  55.     auto end() const {
  56.         return pages_.end();
  57.     }
  58.     int size() const {
  59.         return page_size_;
  60.     }
  61.  
  62. private:
  63.     int page_size_;
  64.     vector<IteratorRange<Iterator>> pages_;
  65. };
  66.  
  67. template <typename Container>
  68. auto Paginate(const Container& c, size_t page_size) {
  69.     return Paginator(begin(c), end(c), page_size);
  70. }
  71.  
  72. template<typename Iterator>
  73. ostream& operator<< (ostream& out, IteratorRange<Iterator> p) {
  74.     for (auto i = p.begin; i < p.end; i++) {
  75.         out << *i;
  76.     }
  77.     return out;
  78. }
  79.  
  80. ***************************************************************************************************************************************
  81.  
  82. read_input_functions.h
  83.  
  84. #pragma once
  85. #include <iostream>
  86. #include <vector>
  87. #include <string>
  88.  
  89. std::string ReadLine();
  90.  
  91. int ReadLineWithNumber();
  92.  
  93. std::vector<std::string> SplitIntoWords(const std::string&);
  94.  
  95. ***************************************************************************************************************************************
  96.  
  97. remove_duplicates.h
  98.  
  99. #pragma once
  100. #include "search_server.h"
  101.  
  102.  
  103. void RemoveDuplicates(SearchServer& search_server);
  104.  
  105. ***************************************************************************************************************************************
  106.  
  107. request_queue.h
  108.  
  109. #pragma once
  110. #include <iostream>
  111. #include <vector>
  112. #include <deque>
  113. #include "document.h"
  114. #include "search_server.h"
  115.  
  116. class RequestQueue {
  117. public:
  118.     explicit RequestQueue(const SearchServer& search_server);
  119.  
  120.     // сделаем "обертки" для всех методов поиска, чтобы сохранять результаты для нашей статистики
  121.     template <typename DocumentPredicate>
  122.     std::vector<Document> AddFindRequest(const string& raw_query, DocumentPredicate document_predicate) {
  123.         const auto result = search_server_.FindTopDocuments(raw_query, document_predicate);
  124.         AddRequest(result.size());
  125.         return result;
  126.     }
  127.     vector<Document> AddFindRequest(const string& raw_query, DocumentStatus status);/* {
  128.         const auto result = search_server_.FindTopDocuments(raw_query, status);
  129.         AddRequest(result.size());
  130.         return result;
  131.     }*/
  132.     vector<Document> AddFindRequest(const string& raw_query); /*{
  133.         const auto result = search_server_.FindTopDocuments(raw_query);
  134.         AddRequest(result.size());
  135.         return result;
  136.     }*/
  137.     int GetNoResultRequests() const;
  138.  
  139. private:
  140.     struct QueryResult {
  141.         uint64_t timestamp;
  142.         int results;
  143.     };
  144.     std::deque<QueryResult> requests_;
  145.     const SearchServer& search_server_;
  146.     int no_results_requests_;
  147.     uint64_t current_time_;
  148.     const static int min_in_day_ = 1440;
  149.  
  150.     void AddRequest(int results_num);
  151. };
  152.  
  153. ***************************************************************************************************************************************
  154.  
  155. search_server.h
  156.  
  157. #pragma once
  158. #include <iostream>
  159. #include <algorithm>
  160. #include <map>
  161. #include <cmath>
  162. #include <vector>
  163. #include "document.h"
  164. #include "read_input_functions.h"
  165. #include "string_processing.h"
  166.  
  167.  
  168. const int MAX_RESULT_DOCUMENT_COUNT = 5;
  169. const double VALUE = 1e-6;
  170.  
  171. class SearchServer {
  172. public:
  173.     template <typename StringContainer>
  174.     explicit SearchServer(const StringContainer& stop_words);
  175.  
  176.     explicit SearchServer(const string& stop_words_text);
  177.  
  178.     void AddDocument(int document_id, const string& document, DocumentStatus status,
  179.         const vector<int>& ratings);
  180.  
  181.     template <typename DocumentPredicate>
  182.     vector<Document> FindTopDocuments(const string& raw_query,
  183.         DocumentPredicate document_predicate) const;
  184.  
  185.     vector<Document> FindTopDocuments(const string& raw_query, DocumentStatus status) const;
  186.  
  187.     vector<Document> FindTopDocuments(const string& raw_query) const;
  188.  
  189.     int GetDocumentCount() const;
  190.  
  191.     set<int>::const_iterator begin() const;
  192.     set<int>::const_iterator end() const;
  193.  
  194.     tuple<vector<string>, DocumentStatus> MatchDocument(const string& raw_query,
  195.         int document_id) const;
  196.     const map<string, double>& GetWordFrequencies(int document_id) const;
  197.     void RemoveDocument(int document_id);
  198.  
  199.     bool IsStopWord(const string& word) const;
  200.  
  201. private:
  202.     struct DocumentData {
  203.         int rating;
  204.         DocumentStatus status;
  205.     };
  206.     const set<string> stop_words_;
  207.     map<string, map<int, double>> word_to_document_freqs_;
  208.     map<int, DocumentData> documents_;
  209.     set<int> document_ids_;
  210.     map<int, map<string, double>> remove_word_to_document_freqs_;
  211.  
  212.  
  213.  
  214.     static bool IsValidWord(const string& word);
  215.  
  216.     vector<string> SplitIntoWordsNoStop(const string& text) const;
  217.  
  218.     static int ComputeAverageRating(const vector<int>& ratings);
  219.  
  220.     struct QueryWord {
  221.         string data;
  222.         bool is_minus;
  223.         bool is_stop;
  224.     };
  225.  
  226.     QueryWord ParseQueryWord(const string& text) const;
  227.  
  228.     struct Query {
  229.         set<string> plus_words;
  230.         set<string> minus_words;
  231.     };
  232.  
  233.     Query ParseQuery(const string& text) const;
  234.  
  235.     // Existence required
  236.     double ComputeWordInverseDocumentFreq(const string& word) const;
  237.  
  238.     template <typename DocumentPredicate>
  239.     vector<Document> FindAllDocuments(const Query& query,
  240.         DocumentPredicate document_predicate) const;
  241. };
  242.  
  243. template <typename StringContainer>
  244. SearchServer::SearchServer(const StringContainer& stop_words)
  245.     : stop_words_(MakeUniqueNonEmptyStrings(stop_words))  // Extract non-empty stop words
  246. {
  247.     if (!all_of(stop_words_.begin(), stop_words_.end(), IsValidWord)) {
  248.         throw invalid_argument("Some of stop words are invalid"s);
  249.     }
  250. }
  251.  
  252. template <typename DocumentPredicate>
  253. vector<Document> SearchServer::FindTopDocuments(const string& raw_query,
  254.     DocumentPredicate document_predicate) const {
  255.     const auto query = ParseQuery(raw_query);
  256.  
  257.     auto matched_documents = FindAllDocuments(query, document_predicate);
  258.  
  259.     sort(matched_documents.begin(), matched_documents.end(), [](const Document& lhs, const Document& rhs) {
  260.         if (std::abs(lhs.relevance - rhs.relevance) < VALUE) {
  261.             return lhs.rating > rhs.rating;
  262.         }
  263.         else {
  264.             return lhs.relevance > rhs.relevance;
  265.         }
  266.         });
  267.     if (matched_documents.size() > MAX_RESULT_DOCUMENT_COUNT) {
  268.         matched_documents.resize(MAX_RESULT_DOCUMENT_COUNT);
  269.     }
  270.  
  271.     return matched_documents;
  272. }
  273.  
  274. template <typename DocumentPredicate>
  275. vector<Document> SearchServer::FindAllDocuments(const Query& query,
  276.     DocumentPredicate document_predicate) const {
  277.     map<int, double> document_to_relevance;
  278.     for (const string& word : query.plus_words) {
  279.         if (word_to_document_freqs_.count(word) == 0) {
  280.             continue;
  281.         }
  282.         const double inverse_document_freq = ComputeWordInverseDocumentFreq(word);
  283.         for (const auto& [document_id, term_freq] : word_to_document_freqs_.at(word)) {
  284.             const auto& document_data = documents_.at(document_id);
  285.             if (document_predicate(document_id, document_data.status, document_data.rating)) {
  286.                 document_to_relevance[document_id] += term_freq * inverse_document_freq;
  287.             }
  288.         }
  289.     }
  290.     for (const string& word : query.minus_words) {
  291.         if (word_to_document_freqs_.count(word) == 0) {
  292.             continue;
  293.         }
  294.         for (const auto& [document_id, _] : word_to_document_freqs_.at(word)) {
  295.             document_to_relevance.erase(document_id);
  296.         }
  297.     }
  298.  
  299.     vector<Document> matched_documents;
  300.     for (const auto& [document_id, relevance] : document_to_relevance) {
  301.         matched_documents.push_back(
  302.             { document_id, relevance, documents_.at(document_id).rating });
  303.     }
  304.     return matched_documents;
  305. }
  306.  
  307. ***************************************************************************************************************************************
  308.  
  309. string_processing.h
  310.  
  311. #pragma once
  312. #include <iostream>
  313. #include <set>
  314. #include "document.h"
  315.  
  316. using namespace std;
  317.  
  318. template <typename StringContainer>
  319. std::set<std::string> MakeUniqueNonEmptyStrings(const StringContainer& strings) {
  320.     std::set<std::string> non_empty_strings;
  321.     for (const std::string& str : strings) {
  322.         if (!str.empty()) {
  323.             non_empty_strings.insert(str);
  324.         }
  325.     }
  326.     return non_empty_strings;
  327. }
  328.  
  329. std::ostream& operator << (std::ostream& out, const Document search);
  330.  
  331. ***************************************************************************************************************************************
  332.  
  333. document.cpp
  334.  
  335. #include "document.h"
  336.  
  337. Document::Document(int id, double relevance, int rating)
  338.     : id(id)
  339.     , relevance(relevance)
  340.     , rating(rating) {
  341. }
  342.  
  343. ***************************************************************************************************************************************
  344.  
  345. main.cpp
  346.  
  347. #include "search_server.h"
  348. #include "request_queue.h"
  349. #include "paginator.h"
  350. #include "remove_duplicates.h"
  351.  
  352.  
  353. int main() {
  354.     SearchServer search_server("and with"s);
  355.     search_server.AddDocument(1, "funny pet and nasty rat"s, DocumentStatus::ACTUAL, { 7, 2, 7 });
  356.     search_server.AddDocument(2, "funny pet with curly hair"s, DocumentStatus::ACTUAL, { 1, 2 });
  357.  
  358.     // дубликат документа 2, будет удалён
  359.     search_server.AddDocument(3, "funny pet with curly hair"s, DocumentStatus::ACTUAL, { 1, 2 });
  360.  
  361.     // отличие только в стоп-словах, считаем дубликатом
  362.     search_server.AddDocument(4, "funny pet and curly hair"s, DocumentStatus::ACTUAL, { 1, 2 });
  363.  
  364.     // множество слов такое же, считаем дубликатом документа 1
  365.     search_server.AddDocument(5, "funny funny pet and nasty nasty rat"s, DocumentStatus::ACTUAL, { 1, 2 });
  366.  
  367.     // добавились новые слова, дубликатом не является
  368.     search_server.AddDocument(6, "funny pet and not very nasty rat"s, DocumentStatus::ACTUAL, { 1, 2 });
  369.  
  370.     // множество слов такое же, как в id 6, несмотря на другой порядок, считаем дубликатом
  371.     search_server.AddDocument(7, "very nasty rat and not very funny pet"s, DocumentStatus::ACTUAL, { 1, 2 });
  372.  
  373.     // есть не все слова, не является дубликатом
  374.     search_server.AddDocument(8, "pet with rat and rat and rat"s, DocumentStatus::ACTUAL, { 1, 2 });
  375.  
  376.     // слова из разных документов, не является дубликатом
  377.     search_server.AddDocument(9, "nasty rat with curly hair"s, DocumentStatus::ACTUAL, { 1, 2 });
  378.  
  379.     cout << "Before duplicates removed: "s << search_server.GetDocumentCount() << endl;
  380.     RemoveDuplicates(search_server);
  381.     cout << "After duplicates removed: "s << search_server.GetDocumentCount() << endl;
  382. }
  383.  
  384. ***************************************************************************************************************************************
  385.  
  386. read_input_functions.cpp
  387.  
  388. #include "read_input_functions.h"
  389.  
  390. std::string ReadLine() {
  391.     std::string s;
  392.     std::getline(std::cin, s);
  393.     return s;
  394. }
  395.  
  396. int ReadLineWithNumber() {
  397.     int result;
  398.     std::cin >> result;
  399.     ReadLine();
  400.     return result;
  401. }
  402.  
  403. std::vector<std::string> SplitIntoWords(const std::string& text) {
  404.     std::vector<std::string> words;
  405.     std::string word;
  406.     for (const char c : text) {
  407.         if (c == ' ') {
  408.             if (!word.empty()) {
  409.                 words.push_back(word);
  410.                 word.clear();
  411.             }
  412.         }
  413.         else {
  414.             word += c;
  415.         }
  416.     }
  417.     if (!word.empty()) {
  418.         words.push_back(word);
  419.     }
  420.  
  421.     return words;
  422. }
  423.  
  424. ***************************************************************************************************************************************
  425.  
  426. remove_duplicates.cpp
  427.  
  428. #include "remove_duplicates.h"
  429.  
  430. void RemoveDuplicates(SearchServer& search_server) {
  431.    
  432.     std::set<int> id_remove;
  433.     map<set<string>, int> unique_doc;
  434.     for (const auto& document_id : search_server)
  435.     {
  436.         map<string, double> words_in_browse = search_server.GetWordFrequencies(document_id);        // получил документ
  437.  
  438.         set<string> uniq_word;
  439.  
  440.         for (const auto& [word, stat] : words_in_browse)
  441.         {
  442.             uniq_word.insert(word);
  443.         }
  444.        
  445.         if (unique_doc.count(uniq_word))
  446.         {
  447.             id_remove.insert(document_id);
  448.            
  449.         }
  450.         else
  451.         {
  452.             unique_doc.insert({ uniq_word, document_id });
  453.         }
  454.  
  455.     }
  456.  
  457.     for (const auto& l : id_remove)
  458.     {
  459.         cout << "Found duplicate document id " << l << endl;
  460.         search_server.RemoveDocument(l);
  461.     }
  462. }
  463. ***************************************************************************************************************************************
  464.  
  465. request_queue.cpp
  466.  
  467. #include "request_queue.h"
  468.  
  469. RequestQueue::RequestQueue(const SearchServer& search_server)
  470.     : search_server_(search_server)
  471.     , no_results_requests_(0)
  472.     , current_time_(0) {
  473. }
  474.  
  475. vector<Document> RequestQueue::AddFindRequest(const string& raw_query) {
  476.     const auto result = search_server_.FindTopDocuments(raw_query);
  477.     AddRequest(result.size());
  478.     return result;
  479. }
  480.  
  481. vector<Document> RequestQueue::AddFindRequest(const string& raw_query, DocumentStatus status) {
  482.     const auto result = search_server_.FindTopDocuments(raw_query, status);
  483.     AddRequest(result.size());
  484.     return result;
  485. }
  486.  
  487. int RequestQueue::GetNoResultRequests() const {
  488.     return no_results_requests_;
  489. }
  490.  
  491. void RequestQueue::AddRequest(int results_num) {
  492.     // новый запрос - новая секунда
  493.     ++current_time_;
  494.     // удаляем все результаты поиска, которые устарели
  495.     while (!requests_.empty() && min_in_day_ <= current_time_ - requests_.front().timestamp) {
  496.         if (0 == requests_.front().results) {
  497.             --no_results_requests_;
  498.         }
  499.         requests_.pop_front();
  500.     }
  501.     // сохраняем новый результат поиска
  502.     requests_.push_back({ current_time_, results_num });
  503.     if (0 == results_num) {
  504.         ++no_results_requests_;
  505.     }
  506. }
  507.  
  508. ***************************************************************************************************************************************
  509.  
  510. search_server.cpp
  511.  
  512. #include "search_server.h"
  513.  
  514. SearchServer::SearchServer(const string& stop_words_text)
  515.     : SearchServer(
  516.         SplitIntoWords(stop_words_text))  // Invoke delegating constructor from string container
  517. {
  518. }
  519.  
  520. void SearchServer::AddDocument(int document_id, const string& document, DocumentStatus status,
  521.     const vector<int>& ratings) {
  522.     if ((document_id < 0) || (documents_.count(document_id) > 0)) {
  523.         throw invalid_argument("Invalid document_id"s);
  524.     }
  525.     const auto words = SplitIntoWordsNoStop(document);
  526.  
  527.     const double inv_word_count = 1.0 / words.size();
  528.     for (const string& word : words) {
  529.         remove_word_to_document_freqs_[document_id][word] += inv_word_count;
  530.         word_to_document_freqs_[word][document_id] += inv_word_count;
  531.     }
  532.     documents_.emplace(document_id, DocumentData{ ComputeAverageRating(ratings), status });
  533.     document_ids_.insert(document_id);
  534. }
  535.  
  536. vector<Document> SearchServer::FindTopDocuments(const string& raw_query, DocumentStatus status) const {
  537.     return FindTopDocuments(
  538.         raw_query, [status](int document_id, DocumentStatus document_status, int rating) {
  539.             return document_status == status;
  540.         });
  541. }
  542.  
  543. vector<Document> SearchServer::FindTopDocuments(const string& raw_query) const {
  544.     return FindTopDocuments(raw_query, DocumentStatus::ACTUAL);
  545. }
  546.  
  547. int SearchServer::GetDocumentCount() const {
  548.     return documents_.size();
  549. }
  550.  
  551. const map<string, double>& SearchServer::GetWordFrequencies(int document_id) const
  552. {
  553.     map <string, double> empty_map;
  554.  
  555.     if (remove_word_to_document_freqs_.count(document_id))
  556.     {
  557.         return remove_word_to_document_freqs_.at(document_id);
  558.     }
  559.  
  560.     return empty_map;
  561. }
  562.  
  563. void SearchServer::RemoveDocument(int document_id)
  564. {
  565.     if (document_ids_.find(document_id) != document_ids_.end())
  566.     {
  567.         for (const auto& [word, stat] : remove_word_to_document_freqs_[document_id])
  568.         {
  569.             auto erase_word = word_to_document_freqs_[word].find(document_id);      // нахожу слово в нужном мне документе
  570.             word_to_document_freqs_[word].erase(erase_word);                        // удаляю данные к слову из документа
  571.         }
  572.     }
  573.  
  574.     documents_.erase(document_id);
  575.     document_ids_.erase(document_id);
  576.     remove_word_to_document_freqs_.erase(document_id);
  577. }
  578.  
  579. set<int>::const_iterator SearchServer::begin() const
  580. {
  581.     return document_ids_.begin();
  582. }
  583.  
  584. set<int>::const_iterator SearchServer::end() const
  585. {
  586.     return document_ids_.end();
  587. }
  588.  
  589. tuple<vector<string>, DocumentStatus> SearchServer::MatchDocument(const string& raw_query,
  590.     int document_id) const {
  591.     const auto query = ParseQuery(raw_query);
  592.  
  593.     vector<string> matched_words;
  594.     for (const string& word : query.plus_words) {
  595.         if (word_to_document_freqs_.count(word) == 0) {
  596.             continue;
  597.         }
  598.         if (word_to_document_freqs_.at(word).count(document_id)) {
  599.             matched_words.push_back(word);
  600.         }
  601.     }
  602.     for (const string& word : query.minus_words) {
  603.         if (word_to_document_freqs_.count(word) == 0) {
  604.             continue;
  605.         }
  606.         if (word_to_document_freqs_.at(word).count(document_id)) {
  607.             matched_words.clear();
  608.             break;
  609.         }
  610.     }
  611.     return { matched_words, documents_.at(document_id).status };
  612. }
  613.  
  614. bool SearchServer::IsStopWord(const string& word) const {
  615.     return stop_words_.count(word) > 0;
  616. }
  617.  
  618. bool SearchServer::IsValidWord(const string& word) {
  619.     // A valid word must not contain special characters
  620.     return none_of(word.begin(), word.end(), [](char c) {
  621.         return c >= '\0' && c < ' ';
  622.         });
  623. }
  624.  
  625. vector<string> SearchServer::SplitIntoWordsNoStop(const string& text) const {
  626.     vector<string> words;
  627.     for (const string& word : SplitIntoWords(text)) {
  628.         if (!IsValidWord(word)) {
  629.             throw invalid_argument("Word "s + word + " is invalid"s);
  630.         }
  631.         if (!IsStopWord(word)) {
  632.             words.push_back(word);
  633.         }
  634.     }
  635.     return words;
  636. }
  637.  
  638. int SearchServer::ComputeAverageRating(const vector<int>& ratings) {
  639.     if (ratings.empty()) {
  640.         return 0;
  641.     }
  642.     int rating_sum = 0;
  643.     for (const int rating : ratings) {
  644.         rating_sum += rating;
  645.     }
  646.     return rating_sum / static_cast<int>(ratings.size());
  647. }
  648.  
  649. SearchServer::QueryWord SearchServer::ParseQueryWord(const string& text) const {
  650.     if (text.empty()) {
  651.         throw invalid_argument("Query word is empty"s);
  652.     }
  653.     string word = text;
  654.     bool is_minus = false;
  655.     if (word[0] == '-') {
  656.         is_minus = true;
  657.         word = word.substr(1);
  658.     }
  659.     if (word.empty() || word[0] == '-' || !IsValidWord(word)) {
  660.         throw invalid_argument("Query word "s + text + " is invalid");
  661.     }
  662.  
  663.     return { word, is_minus, IsStopWord(word) };
  664. }
  665.  
  666. SearchServer::Query SearchServer::ParseQuery(const string& text) const {
  667.     Query result;
  668.     for (const string& word : SplitIntoWords(text)) {
  669.         const auto query_word = ParseQueryWord(word);
  670.         if (!query_word.is_stop) {
  671.             if (query_word.is_minus) {
  672.                 result.minus_words.insert(query_word.data);
  673.             }
  674.             else {
  675.                 result.plus_words.insert(query_word.data);
  676.             }
  677.         }
  678.     }
  679.     return result;
  680. }
  681.  
  682. double SearchServer::ComputeWordInverseDocumentFreq(const std::string& word) const {
  683.     return std::log(GetDocumentCount() * 1.0 /word_to_document_freqs_.at(word).size());
  684. }
  685.  
  686. ***************************************************************************************************************************************
  687.  
  688. string_processing.cpp
  689.  
  690. #include "string_processing.h"
  691.  
  692.  
  693. std::ostream& operator << (std::ostream& out, const Document search) {
  694.     return out << "{ document_id = " << search.id << ", relevance = " << search.relevance << ", rating = " << search.rating << " }";
  695. }
  696.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement