zimovid

Untitled

Jul 5th, 2023 (edited)
696
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 13.42 KB | None | 0 0
  1. #include <cassert>
  2. #include <algorithm>
  3. #include <cmath>
  4. #include <iostream>
  5. #include <map>
  6. #include <numeric>
  7. #include <set>
  8. #include <string>
  9. #include <utility>
  10. #include <vector>
  11.  
  12. using namespace std;
  13.  
  14. const int MAX_RESULT_DOCUMENT_COUNT = 5;
  15. const double EPSILON = numeric_limits<double>::epsilon();
  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.   int id;
  52.   double relevance;
  53.   int rating;
  54. };
  55.  
  56. enum class DocumentStatus {
  57.   ACTUAL,
  58.   IRRELEVANT,
  59.   BANNED,
  60.   REMOVED,
  61. };
  62.  
  63. class SearchServer {
  64.  public:
  65.   void SetStopWords(const string& text) {
  66.     for (const string& word : SplitIntoWords(text)) {
  67.       stop_words_.insert(word);
  68.     }
  69.   }
  70.  
  71.   void AddDocument(int document_id, const string& document,
  72.                    DocumentStatus status, const vector<int>& ratings) {
  73.     const vector<string> words = SplitIntoWordsNoStop(document);
  74.     const double inv_word_count = 1.0 / words.size();
  75.     for (const string& word : words) {
  76.       word_to_document_freqs_[word][document_id] += inv_word_count;
  77.     }
  78.     documents_.emplace(document_id,
  79.                        DocumentData{ComputeAverageRating(ratings), status});
  80.   }
  81.  
  82.   vector<Document> FindTopDocuments(const string& raw_query,
  83.                                     DocumentStatus status) const {
  84.     return FindTopDocuments(
  85.         raw_query, [status](int document_id [[maybe_unused]], DocumentStatus document_status,
  86.                             int rating [[maybe_unused]]) { return document_status == status; });
  87.   }
  88.  
  89.   vector<Document> FindTopDocuments(const string& raw_query) const {
  90.     return FindTopDocuments(
  91.         raw_query, [](int document_id [[maybe_unused]], DocumentStatus status, int rating [[maybe_unused]]) {
  92.           return status == DocumentStatus::ACTUAL;
  93.         });
  94.   }
  95.  
  96.   template <typename Functor>
  97.   vector<Document> FindTopDocuments(const string& raw_query,
  98.                                     Functor functor) const {
  99.     const Query query = ParseQuery(raw_query);
  100.     auto matched_documents = FindAllDocuments(query, functor);
  101.  
  102.     sort(matched_documents.begin(), matched_documents.end(),
  103.          [](const Document& lhs, const Document& rhs) {
  104.            if (abs(lhs.relevance - rhs.relevance) < EPSILON) {
  105.              return lhs.rating > rhs.rating;
  106.            } else {
  107.              return lhs.relevance > rhs.relevance;
  108.            }
  109.          });
  110.  
  111.     if (matched_documents.size() > MAX_RESULT_DOCUMENT_COUNT) {
  112.       matched_documents.resize(MAX_RESULT_DOCUMENT_COUNT);
  113.     }
  114.     return matched_documents;
  115.   }
  116.  
  117.   int GetDocumentCount() const { return documents_.size(); }
  118.  
  119.   tuple<vector<string>, DocumentStatus> MatchDocument(const string& raw_query,
  120.                                                       int document_id) const {
  121.     const Query query = ParseQuery(raw_query);
  122.     vector<string> matched_words;
  123.     for (const string& word : query.plus_words) {
  124.       if (word_to_document_freqs_.count(word) == 0) {
  125.         continue;
  126.       }
  127.       if (word_to_document_freqs_.at(word).count(document_id)) {
  128.         matched_words.push_back(word);
  129.       }
  130.     }
  131.     for (const string& word : query.minus_words) {
  132.       if (word_to_document_freqs_.count(word) == 0) {
  133.         continue;
  134.       }
  135.       if (word_to_document_freqs_.at(word).count(document_id)) {
  136.         matched_words.clear();
  137.         break;
  138.       }
  139.     }
  140.     return {matched_words, documents_.at(document_id).status};
  141.   }
  142.  
  143.  private:
  144.   struct DocumentData {
  145.     int rating;
  146.     DocumentStatus status;
  147.   };
  148.  
  149.   set<string> stop_words_;
  150.   map<string, map<int, double>> word_to_document_freqs_;
  151.   map<int, DocumentData> documents_;
  152.  
  153.   bool IsStopWord(const string& word) const {
  154.     return stop_words_.count(word) > 0;
  155.   }
  156.  
  157.   vector<string> SplitIntoWordsNoStop(const string& text) const {
  158.     vector<string> words;
  159.     for (const string& word : SplitIntoWords(text)) {
  160.       if (!IsStopWord(word)) {
  161.         words.push_back(word);
  162.       }
  163.     }
  164.     return words;
  165.   }
  166.  
  167.   static int ComputeAverageRating(const vector<int>& ratings) {
  168.     if (ratings.empty()) {
  169.       return 0;
  170.     }
  171.     int rating_sum = accumulate(ratings.begin(), ratings.end(), 0);
  172.     return rating_sum / static_cast<int>(ratings.size());
  173.   }
  174.  
  175.   struct QueryWord {
  176.     string data;
  177.     bool is_minus;
  178.     bool is_stop;
  179.   };
  180.  
  181.   QueryWord ParseQueryWord(string text) const {
  182.     bool is_minus = false;
  183.     // Word shouldn't be empty
  184.     if (text[0] == '-') {
  185.       is_minus = true;
  186.       text = text.substr(1);
  187.     }
  188.     return {text, is_minus, IsStopWord(text)};
  189.   }
  190.  
  191.   struct Query {
  192.     set<string> plus_words;
  193.     set<string> minus_words;
  194.   };
  195.  
  196.   Query ParseQuery(const string& text) const {
  197.     Query query;
  198.     for (const string& word : SplitIntoWords(text)) {
  199.       const QueryWord query_word = ParseQueryWord(word);
  200.       if (!query_word.is_stop) {
  201.         if (query_word.is_minus) {
  202.           query.minus_words.insert(query_word.data);
  203.         } else {
  204.           query.plus_words.insert(query_word.data);
  205.         }
  206.       }
  207.     }
  208.     return query;
  209.   }
  210.  
  211.   double ComputeWordInverseDocumentFreq(const string& word) const {
  212.     return log(GetDocumentCount() * 1.0 /
  213.                word_to_document_freqs_.at(word).size());
  214.   }
  215.  
  216.   vector<Document> FindAllDocuments(
  217.       const Query& query,
  218.       function<bool(int, DocumentStatus, int)> predicate) const {
  219.     map<int, double> document_to_relevance;
  220.     for (const string& word : query.plus_words) {
  221.       if (word_to_document_freqs_.count(word) == 0) {
  222.         continue;
  223.       }
  224.       const double inverse_document_freq = ComputeWordInverseDocumentFreq(word);
  225.       for (const auto [document_id, term_freq] :
  226.            word_to_document_freqs_.at(word)) {
  227.         const auto& document_data = documents_.at(document_id);
  228.         if (predicate(document_id, document_data.status,
  229.                       document_data.rating)) {
  230.           document_to_relevance[document_id] +=
  231.               term_freq * inverse_document_freq;
  232.         }
  233.       }
  234.     }
  235.  
  236.     for (const string& word : query.minus_words) {
  237.       if (word_to_document_freqs_.count(word) == 0) {
  238.         continue;
  239.       }
  240.       for (const auto [document_id, _] : word_to_document_freqs_.at(word)) {
  241.         document_to_relevance.erase(document_id);
  242.       }
  243.     }
  244.  
  245.     vector<Document> matched_documents;
  246.     for (const auto [document_id, relevance] : document_to_relevance) {
  247.       matched_documents.push_back(
  248.           {document_id, relevance, documents_.at(document_id).rating});
  249.     }
  250.     return matched_documents;
  251.   }
  252. };
  253.  
  254. void PrintDocument(const Document& document) {
  255.   cout << "{ "s
  256.        << "document_id = "s << document.id << ", "s
  257.        << "relevance = "s << document.relevance << ", "s
  258.        << "rating = "s << document.rating << " }"s << endl;
  259. }
  260.  
  261.  
  262. /* Подставьте вашу реализацию класса SearchServer сюда */
  263.  
  264. // -------- Начало модульных тестов поисковой системы ----------
  265.  
  266. // -------- Начало модульных тестов поисковой системы ----------
  267.  
  268. // Тест проверяет, что поисковая система исключает стоп-слова при добавлении документов
  269. void TestExcludeStopWordsFromAddedDocumentContent() {
  270.     const int doc_id = 42;
  271.     const string content = "cat in the city"s;
  272.     const vector<int> ratings = {1, 2, 3};
  273.     SearchServer server;
  274.     server.AddDocument(doc_id, content, DocumentStatus::ACTUAL, ratings);
  275.     const auto found_docs = server.FindTopDocuments("in"s);
  276.     // Сначала убеждаемся, что поиск слова, не входящего в список стоп-слов,
  277.     // находит нужный документ
  278.     {
  279.         const Document& doc0 = found_docs[0];
  280.         assert(found_docs.size() == 1);
  281.         assert(doc0.id == doc_id);
  282.     }
  283.  
  284.     // Затем убеждаемся, что поиск этого же слова, входящего в список стоп-слов,
  285.     // возвращает пустой результат
  286.     {
  287.         assert(found_docs.empty());
  288.     }
  289. }
  290.  
  291. void TestFindTopDocuments() {
  292.   SearchServer search_server;
  293.   search_server.AddDocument(0, "white cat and fashionable collar", DocumentStatus::ACTUAL, {8, -3});
  294.   search_server.AddDocument(1, "fluffy cat fluffy tail", DocumentStatus::ACTUAL, {7, 2, 7});
  295.   search_server.AddDocument(2, "groomed dog expressive eyes", DocumentStatus::BANNED, {5, -12, 2, 1});
  296.  
  297.     {
  298.       const auto documents = search_server.FindTopDocuments("fluffy groomed cat");
  299.       assert(documents.size() == 2);  // Only two documents with status ACTUAL should be returned
  300.     }
  301.    
  302.     {
  303.          const auto documents = search_server.FindTopDocuments("fluffy groomed cat", DocumentStatus::BANNED);
  304.           assert(documents.size() == 1);
  305.     }
  306.    
  307.     {
  308.          auto predicate = [](int document_id, DocumentStatus status [[maybe_unused]], int rating [[maybe_unused]]) {
  309.             return document_id % 2 == 0;  // Only accept documents with even ids
  310.           };
  311.           const auto documents = search_server.FindTopDocuments("fluffy groomed cat", predicate);
  312.           assert(documents.size() == 2);
  313.     }
  314.  
  315. }
  316.  
  317. /*
  318. Разместите код остальных тестов здесь
  319. */
  320.  
  321. void TestMatchDocument() {
  322.   SearchServer search_server;
  323.   search_server.AddDocument(0, "white cat and fashionable collar"s,
  324.                             DocumentStatus::ACTUAL, {8, -3});
  325.   auto [words, status] = search_server.MatchDocument("white cat"s, 0);
  326.   assert(words.size() == 2);
  327.   assert(words[0] == "cat");
  328.   assert(words[1] == "white");
  329. }
  330.  
  331. void TestStopWords() {
  332.   SearchServer search_server;
  333.   search_server.SetStopWords("and"s);
  334.   search_server.AddDocument(0, "white cat and fashionable collar"s,
  335.                             DocumentStatus::ACTUAL, {8, -3});
  336.   vector<Document> results = search_server.FindTopDocuments("and"s);
  337.   assert(results.empty());
  338. }
  339.  
  340. void TestMinusWords() {
  341.   SearchServer search_server;
  342.   search_server.AddDocument(0, "white cat and fashionable collar"s,
  343.                             DocumentStatus::ACTUAL, {8, -3});
  344.   vector<Document> results = search_server.FindTopDocuments("cat -white"s);
  345.   assert(results.empty());
  346. }
  347.  
  348. void TestSortByRelevance() {
  349.   SearchServer search_server;
  350.   search_server.AddDocument(0, "white cat and fashionable collar"s,
  351.                             DocumentStatus::ACTUAL, {8, -3});
  352.   search_server.AddDocument(1, "white cat and fashionable collar"s,
  353.                             DocumentStatus::ACTUAL, {8, -3, 10});
  354.   vector<Document> results = search_server.FindTopDocuments("white cat"s);
  355.   assert(results.size() == 2);
  356.   assert(results[0].id == 1);
  357.   assert(results[1].id == 0);
  358. }
  359.  
  360. void TestComputeAverageRating() {
  361.   SearchServer search_server;
  362.   search_server.AddDocument(0, "white cat and fashionable collar"s,
  363.                             DocumentStatus::ACTUAL, {8, -3, 10});
  364.   vector<Document> results = search_server.FindTopDocuments("white cat"s);  
  365.   assert(results.at(0).rating == 5);
  366. }
  367.  
  368. void TestPredicateFiltering() {
  369.   SearchServer search_server;
  370.   search_server.AddDocument(0, "white cat and fashionable collar"s,
  371.                             DocumentStatus::ACTUAL, {8, -3});
  372.   search_server.AddDocument(1, "white cat and fashionable collar"s,
  373.                             DocumentStatus::ACTUAL, {8, -3, 10});
  374.   vector<Document> results = search_server.FindTopDocuments(
  375.       "white cat"s, [](int document_id, DocumentStatus , int ) {
  376.         return document_id % 2 == 0;
  377.       });
  378.   assert(results.size() == 1);
  379.   assert(results[0].id == 0);
  380. }
  381.  
  382. void TestStatusFiltering() {
  383.   SearchServer search_server;
  384.   search_server.AddDocument(0, "white cat and fashionable collar"s,
  385.                             DocumentStatus::ACTUAL, {8, -3});
  386.   search_server.AddDocument(1, "white cat and fashionable collar"s,
  387.                             DocumentStatus::BANNED, {8, -3, 10});
  388.   vector<Document> results = search_server.FindTopDocuments(
  389.       "white cat"s, DocumentStatus::ACTUAL);
  390.   assert(results.size() == 1);
  391.   assert(results[0].id == 0);
  392. }
  393.  
  394. // Функция TestSearchServer является точкой входа для запуска тестов
  395. void TestSearchServer() {
  396.     TestExcludeStopWordsFromAddedDocumentContent();
  397.     TestFindTopDocuments();
  398.     TestMatchDocument();
  399.     TestStopWords();
  400.     TestMinusWords();
  401.     TestSortByRelevance();
  402.     TestComputeAverageRating();
  403.     TestPredicateFiltering();
  404.     TestStatusFiltering();
  405.     // Не забудьте вызывать остальные тесты здесь
  406. }
  407.  
  408. // --------- Окончание модульных тестов поисковой системы -----------
  409.  
  410. int main() {
  411.     TestSearchServer();
  412.     // Если вы видите эту строку, значит все тесты прошли успешно
  413.     cout << "Search server testing finished"s << endl;
  414. }
Advertisement
Add Comment
Please, Sign In to add comment