Advertisement
kutuzzzov

Спринт 4. Урок 9. Выводим результаты поиска страницами

Dec 27th, 2021
665
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 14.34 KB | None | 0 0
  1. // search_server_s4_t2_solution.cpp
  2.  
  3. #include <algorithm>
  4. #include <cmath>
  5. #include <iostream>
  6. #include <map>
  7. #include <set>
  8. #include <stdexcept>
  9. #include <string>
  10. #include <utility>
  11. #include <vector>
  12.  
  13. using namespace std;
  14.  
  15. const int MAX_RESULT_DOCUMENT_COUNT = 5;
  16.  
  17. string ReadLine() {
  18.     string s;
  19.     getline(cin, s);
  20.     return s;
  21. }
  22.  
  23. int ReadLineWithNumber() {
  24.     int result;
  25.     cin >> result;
  26.     ReadLine();
  27.     return result;
  28. }
  29.  
  30. vector<string> SplitIntoWords(const string& text) {
  31.     vector<string> words;
  32.     string word;
  33.     for (const char c : text) {
  34.         if (c == ' ') {
  35.             if (!word.empty()) {
  36.                 words.push_back(word);
  37.                 word.clear();
  38.             }
  39.         } else {
  40.             word += c;
  41.         }
  42.     }
  43.     if (!word.empty()) {
  44.         words.push_back(word);
  45.     }
  46.  
  47.     return words;
  48. }
  49.  
  50. struct Document {
  51.     Document() = default;
  52.  
  53.     Document(int id, double relevance, int rating)
  54.         : id(id)
  55.         , relevance(relevance)
  56.         , rating(rating) {
  57.     }
  58.  
  59.     int id = 0;
  60.     double relevance = 0.0;
  61.     int rating = 0;
  62. };
  63.  
  64. ostream& operator<<(ostream& out, const Document& document) {
  65.     out << "{ "s
  66.         << "document_id = "s << document.id << ", "s
  67.         << "relevance = "s << document.relevance << ", "s
  68.         << "rating = "s << document.rating << " }"s;
  69.     return out;
  70. }
  71.  
  72. template <typename StringContainer>
  73. set<string> MakeUniqueNonEmptyStrings(const StringContainer& strings) {
  74.     set<string> non_empty_strings;
  75.     for (const string& str : strings) {
  76.         if (!str.empty()) {
  77.             non_empty_strings.insert(str);
  78.         }
  79.     }
  80.     return non_empty_strings;
  81. }
  82.  
  83. enum class DocumentStatus {
  84.     ACTUAL,
  85.     IRRELEVANT,
  86.     BANNED,
  87.     REMOVED,
  88. };
  89.  
  90. class SearchServer {
  91. public:
  92.     template <typename StringContainer>
  93.     explicit SearchServer(const StringContainer& stop_words)
  94.         : stop_words_(MakeUniqueNonEmptyStrings(stop_words))  // Extract non-empty stop words
  95.     {
  96.         if (!all_of(stop_words_.begin(), stop_words_.end(), IsValidWord)) {
  97.             throw invalid_argument("Some of stop words are invalid"s);
  98.         }
  99.     }
  100.  
  101.     explicit SearchServer(const string& stop_words_text)
  102.         : SearchServer(SplitIntoWords(stop_words_text))  // Invoke delegating constructor
  103.                                                          // from string container
  104.     {
  105.     }
  106.  
  107.     void AddDocument(int document_id, const string& document, DocumentStatus status, const vector<int>& ratings) {
  108.         if ((document_id < 0) || (documents_.count(document_id) > 0)) {
  109.             throw invalid_argument("Invalid document_id"s);
  110.         }
  111.         const auto words = SplitIntoWordsNoStop(document);
  112.  
  113.         const double inv_word_count = 1.0 / words.size();
  114.         for (const string& word : words) {
  115.             word_to_document_freqs_[word][document_id] += inv_word_count;
  116.         }
  117.         documents_.emplace(document_id, DocumentData{ComputeAverageRating(ratings), status});
  118.         document_ids_.push_back(document_id);
  119.     }
  120.  
  121.     template <typename DocumentPredicate>
  122.     vector<Document> FindTopDocuments(const string& raw_query, DocumentPredicate document_predicate) const {
  123.         const auto query = ParseQuery(raw_query);
  124.  
  125.         auto matched_documents = FindAllDocuments(query, document_predicate);
  126.  
  127.         sort(matched_documents.begin(), matched_documents.end(), [](const Document& lhs, const Document& rhs) {
  128.             if (abs(lhs.relevance - rhs.relevance) < 1e-6) {
  129.                 return lhs.rating > rhs.rating;
  130.             } else {
  131.                 return lhs.relevance > rhs.relevance;
  132.             }
  133.         });
  134.         if (matched_documents.size() > MAX_RESULT_DOCUMENT_COUNT) {
  135.             matched_documents.resize(MAX_RESULT_DOCUMENT_COUNT);
  136.         }
  137.  
  138.         return matched_documents;
  139.     }
  140.  
  141.     vector<Document> FindTopDocuments(const string& raw_query, DocumentStatus status) const {
  142.         return FindTopDocuments(raw_query, [status](int document_id, DocumentStatus document_status, int rating) {
  143.             return document_status == status;
  144.         });
  145.     }
  146.  
  147.     vector<Document> FindTopDocuments(const string& raw_query) const {
  148.         return FindTopDocuments(raw_query, DocumentStatus::ACTUAL);
  149.     }
  150.  
  151.     int GetDocumentCount() const {
  152.         return documents_.size();
  153.     }
  154.  
  155.     int GetDocumentId(int index) const {
  156.         return document_ids_.at(index);
  157.     }
  158.  
  159.     tuple<vector<string>, DocumentStatus> MatchDocument(const string& raw_query, int document_id) const {
  160.         const auto query = ParseQuery(raw_query);
  161.  
  162.         vector<string> matched_words;
  163.         for (const string& word : query.plus_words) {
  164.             if (word_to_document_freqs_.count(word) == 0) {
  165.                 continue;
  166.             }
  167.             if (word_to_document_freqs_.at(word).count(document_id)) {
  168.                 matched_words.push_back(word);
  169.             }
  170.         }
  171.         for (const string& word : query.minus_words) {
  172.             if (word_to_document_freqs_.count(word) == 0) {
  173.                 continue;
  174.             }
  175.             if (word_to_document_freqs_.at(word).count(document_id)) {
  176.                 matched_words.clear();
  177.                 break;
  178.             }
  179.         }
  180.         return {matched_words, documents_.at(document_id).status};
  181.     }
  182.  
  183. private:
  184.     struct DocumentData {
  185.         int rating;
  186.         DocumentStatus status;
  187.     };
  188.     const set<string> stop_words_;
  189.     map<string, map<int, double>> word_to_document_freqs_;
  190.     map<int, DocumentData> documents_;
  191.     vector<int> document_ids_;
  192.  
  193.     bool IsStopWord(const string& word) const {
  194.         return stop_words_.count(word) > 0;
  195.     }
  196.  
  197.     static bool IsValidWord(const string& word) {
  198.         // A valid word must not contain special characters
  199.         return none_of(word.begin(), word.end(), [](char c) {
  200.             return c >= '\0' && c < ' ';
  201.         });
  202.     }
  203.  
  204.     vector<string> SplitIntoWordsNoStop(const string& text) const {
  205.         vector<string> words;
  206.         for (const string& word : SplitIntoWords(text)) {
  207.             if (!IsValidWord(word)) {
  208.                 throw invalid_argument("Word "s + word + " is invalid"s);
  209.             }
  210.             if (!IsStopWord(word)) {
  211.                 words.push_back(word);
  212.             }
  213.         }
  214.         return words;
  215.     }
  216.  
  217.     static int ComputeAverageRating(const vector<int>& ratings) {
  218.         if (ratings.empty()) {
  219.             return 0;
  220.         }
  221.         int rating_sum = 0;
  222.         for (const int rating : ratings) {
  223.             rating_sum += rating;
  224.         }
  225.         return rating_sum / static_cast<int>(ratings.size());
  226.     }
  227.  
  228.     struct QueryWord {
  229.         string data;
  230.         bool is_minus;
  231.         bool is_stop;
  232.     };
  233.  
  234.     QueryWord ParseQueryWord(const string& text) const {
  235.         if (text.empty()) {
  236.             throw invalid_argument("Query word is empty"s);
  237.         }
  238.         string word = text;
  239.         bool is_minus = false;
  240.         if (word[0] == '-') {
  241.             is_minus = true;
  242.             word = word.substr(1);
  243.         }
  244.         if (word.empty() || word[0] == '-' || !IsValidWord(word)) {
  245.             throw invalid_argument("Query word "s + text + " is invalid");
  246.         }
  247.  
  248.         return {word, is_minus, IsStopWord(word)};
  249.     }
  250.  
  251.     struct Query {
  252.         set<string> plus_words;
  253.         set<string> minus_words;
  254.     };
  255.  
  256.     Query ParseQuery(const string& text) const {
  257.         Query result;
  258.         for (const string& word : SplitIntoWords(text)) {
  259.             const auto query_word = ParseQueryWord(word);
  260.             if (!query_word.is_stop) {
  261.                 if (query_word.is_minus) {
  262.                     result.minus_words.insert(query_word.data);
  263.                 } else {
  264.                     result.plus_words.insert(query_word.data);
  265.                 }
  266.             }
  267.         }
  268.         return result;
  269.     }
  270.  
  271.     // Existence required
  272.     double ComputeWordInverseDocumentFreq(const string& word) const {
  273.         return log(GetDocumentCount() * 1.0 / word_to_document_freqs_.at(word).size());
  274.     }
  275.  
  276.     template <typename DocumentPredicate>
  277.     vector<Document> FindAllDocuments(const Query& query, DocumentPredicate document_predicate) const {
  278.         map<int, double> document_to_relevance;
  279.         for (const string& word : query.plus_words) {
  280.             if (word_to_document_freqs_.count(word) == 0) {
  281.                 continue;
  282.             }
  283.             const double inverse_document_freq = ComputeWordInverseDocumentFreq(word);
  284.             for (const auto [document_id, term_freq] : word_to_document_freqs_.at(word)) {
  285.                 const auto& document_data = documents_.at(document_id);
  286.                 if (document_predicate(document_id, document_data.status, document_data.rating)) {
  287.                     document_to_relevance[document_id] += term_freq * inverse_document_freq;
  288.                 }
  289.             }
  290.         }
  291.  
  292.         for (const string& word : query.minus_words) {
  293.             if (word_to_document_freqs_.count(word) == 0) {
  294.                 continue;
  295.             }
  296.             for (const auto [document_id, _] : word_to_document_freqs_.at(word)) {
  297.                 document_to_relevance.erase(document_id);
  298.             }
  299.         }
  300.  
  301.         vector<Document> matched_documents;
  302.         for (const auto [document_id, relevance] : document_to_relevance) {
  303.             matched_documents.push_back({document_id, relevance, documents_.at(document_id).rating});
  304.         }
  305.         return matched_documents;
  306.     }
  307. };
  308.  
  309. void PrintDocument(const Document& document) {
  310.     cout << "{ "s
  311.          << "document_id = "s << document.id << ", "s
  312.          << "relevance = "s << document.relevance << ", "s
  313.          << "rating = "s << document.rating << " }"s << endl;
  314. }
  315.  
  316. void PrintMatchDocumentResult(int document_id, const vector<string>& words, DocumentStatus status) {
  317.     cout << "{ "s
  318.          << "document_id = "s << document_id << ", "s
  319.          << "status = "s << static_cast<int>(status) << ", "s
  320.          << "words ="s;
  321.     for (const string& word : words) {
  322.         cout << ' ' << word;
  323.     }
  324.     cout << "}"s << endl;
  325. }
  326.  
  327. void AddDocument(SearchServer& search_server, int document_id, const string& document, DocumentStatus status,
  328.                  const vector<int>& ratings) {
  329.     try {
  330.         search_server.AddDocument(document_id, document, status, ratings);
  331.     } catch (const invalid_argument& e) {
  332.         cout << "Ошибка добавления документа "s << document_id << ": "s << e.what() << endl;
  333.     }
  334. }
  335.  
  336. void FindTopDocuments(const SearchServer& search_server, const string& raw_query) {
  337.     cout << "Результаты поиска по запросу: "s << raw_query << endl;
  338.     try {
  339.         for (const Document& document : search_server.FindTopDocuments(raw_query)) {
  340.             PrintDocument(document);
  341.         }
  342.     } catch (const invalid_argument& e) {
  343.         cout << "Ошибка поиска: "s << e.what() << endl;
  344.     }
  345. }
  346.  
  347. void MatchDocuments(const SearchServer& search_server, const string& query) {
  348.     try {
  349.         cout << "Матчинг документов по запросу: "s << query << endl;
  350.         const int document_count = search_server.GetDocumentCount();
  351.         for (int index = 0; index < document_count; ++index) {
  352.             const int document_id = search_server.GetDocumentId(index);
  353.             const auto [words, status] = search_server.MatchDocument(query, document_id);
  354.             PrintMatchDocumentResult(document_id, words, status);
  355.         }
  356.     } catch (const invalid_argument& e) {
  357.         cout << "Ошибка матчинга документов на запрос "s << query << ": "s << e.what() << endl;
  358.     }
  359. }
  360.  
  361. template <typename Iterator>
  362. class IteratorRange {
  363. public:
  364.     IteratorRange(Iterator begin, Iterator end)
  365.         : first_(begin)
  366.         , last_(end)
  367.         , size_(distance(first_, last_)) {
  368.     }
  369.  
  370.     Iterator begin() const {
  371.         return first_;
  372.     }
  373.  
  374.     Iterator end() const {
  375.         return last_;
  376.     }
  377.  
  378.     size_t size() const {
  379.         return size_;
  380.     }
  381.  
  382. private:
  383.     Iterator first_, last_;
  384.     size_t size_;
  385. };
  386.  
  387. template <typename Iterator>
  388. ostream& operator<<(ostream& out, const IteratorRange<Iterator>& range) {
  389.     for (Iterator it = range.begin(); it != range.end(); ++it) {
  390.         out << *it;
  391.     }
  392.     return out;
  393. }
  394.  
  395. template <typename Iterator>
  396. class Paginator {
  397. public:
  398.     Paginator(Iterator begin, Iterator end, size_t page_size) {
  399.         for (size_t left = distance(begin, end); left > 0;) {
  400.             const size_t current_page_size = min(page_size, left);
  401.             const Iterator current_page_end = next(begin, current_page_size);
  402.             pages_.push_back({begin, current_page_end});
  403.  
  404.             left -= current_page_size;
  405.             begin = current_page_end;
  406.         }
  407.     }
  408.  
  409.     auto begin() const {
  410.         return pages_.begin();
  411.     }
  412.  
  413.     auto end() const {
  414.         return pages_.end();
  415.     }
  416.  
  417.     size_t size() const {
  418.         return pages_.size();
  419.     }
  420.  
  421. private:
  422.     vector<IteratorRange<Iterator>> pages_;
  423. };
  424.  
  425. template <typename Container>
  426. auto Paginate(const Container& c, size_t page_size) {
  427.     return Paginator(begin(c), end(c), page_size);
  428. }
  429.  
  430. int main() {
  431.     SearchServer search_server("и в на"s);
  432.  
  433.     search_server.AddDocument(1, "пушистый кот пушистый хвост"s, DocumentStatus::ACTUAL, {7, 2, 7});
  434.     search_server.AddDocument(2, "пушистый пёс и модный ошейник"s, DocumentStatus::ACTUAL, {1, 2, 3});
  435.     search_server.AddDocument(3, "большой кот модный ошейник "s, DocumentStatus::ACTUAL, {1, 2, 8});
  436.     search_server.AddDocument(4, "большой пёс скворец евгений"s, DocumentStatus::ACTUAL, {1, 3, 2});
  437.     search_server.AddDocument(5, "большой пёс скворец василий"s, DocumentStatus::ACTUAL, {1, 1, 1});
  438.  
  439.     const auto search_results = search_server.FindTopDocuments("пушистый пёс"s);
  440.     int page_size = 2;
  441.     const auto pages = Paginate(search_results, page_size);
  442.  
  443.     // Выводим найденные документы по страницам
  444.     for (auto page = pages.begin(); page != pages.end(); ++page) {
  445.         cout << *page << endl;
  446.         cout << "Разрыв страницы"s << endl;
  447.     }
  448.  
  449.     return 0;
  450. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement