Advertisement
RobertDeMilo

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

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