Advertisement
kutuzzzov

Спринт 3. Ревью

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