Alex_St

Optinal class

Nov 29th, 2022
155
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 14.66 KB | None | 0 0
  1. #include <algorithm>
  2. #include <iostream>
  3. #include <set>
  4. #include <string>
  5. #include <utility>
  6. #include <vector>
  7. #include <map>
  8. #include <cmath>
  9. #include <numeric>
  10. #include <cassert>
  11. #include <tuple>
  12. #include <optional>
  13.  
  14. using namespace std;
  15.  
  16. const int MAX_RESULT_DOCUMENT_COUNT = 5;
  17.  
  18. enum class DocumentStatus {
  19.     ACTUAL,
  20.     IRRELEVANT,
  21.     BANNED,
  22.     REMOVED
  23. };
  24.  
  25. string ReadLine() {
  26.     string s;
  27.     getline(cin, s);
  28.     return s;
  29. }
  30.  
  31. int ReadLineWithNumber() {
  32.     int result = 0;
  33.     cin >> result;
  34.     cin.ignore(50, '\n');
  35.     return result;
  36. }
  37.  
  38. vector<string> SplitIntoWords(const string& text) {
  39.     vector<string> words;
  40.     string word;
  41.     for (const char c : text) {
  42.         if (c == ' ') {
  43.             if (!word.empty()) {
  44.                 words.push_back(word);
  45.                 word.clear();
  46.             }
  47.         }
  48.         else {
  49.             word += c;
  50.         }
  51.     }
  52.     if (!word.empty()) {
  53.         words.push_back(word);
  54.     }
  55.  
  56.     return words;
  57. }
  58.  
  59. vector<int> ReadLineWithRatings()
  60. {
  61.     int num_of_ratings;
  62.     cin >> num_of_ratings;
  63.  
  64.     int number = 0;
  65.     vector<int> temp_result;
  66.     for (int i = 0; i < num_of_ratings; ++i)
  67.     {
  68.         cin >> number;
  69.         temp_result.push_back(number);
  70.     }
  71.  
  72.     cin.ignore(2000, '\n');
  73.  
  74.     return temp_result;
  75. }
  76.  
  77. struct Document  //структура, являющаяся результатом наших вычислений/поисков по заданным критериям
  78. {
  79.     Document()
  80.         :id(0), relevance(0.f), rating(0)
  81.     {
  82.  
  83.     }
  84.  
  85.     Document(int r_id, double r_relevance, int r_rating)
  86.         :id(r_id), relevance(r_relevance), rating(r_rating)
  87.     {
  88.  
  89.     }
  90.  
  91.     int id;
  92.     double relevance;
  93.     int rating;
  94. };
  95.  
  96.  
  97. //ОБЪЯВЛЕНИЕ КЛАССА SearchServer
  98.  
  99. class SearchServer
  100. {
  101. public:
  102.     inline static constexpr int INVALID_DOCUMENT_ID = -1;
  103.  
  104.     SearchServer()
  105.     {
  106.  
  107.     }
  108.  
  109.     SearchServer(const string& raw_string)
  110.     {
  111.         for (const string& word : SplitIntoWords(raw_string))
  112.             stop_words_.insert(word);
  113.     }
  114.  
  115.     template <typename T>
  116.     SearchServer(const T& raw_collection)
  117.     {
  118.         for (const string& word : raw_collection)
  119.             stop_words_.insert(word);
  120.     }
  121.  
  122.     int GetDocumentId(int index) const {
  123.         return ((index >= 0) && (index < document_count_)) ? id_to_docnumber_.at(index) : SearchServer::INVALID_DOCUMENT_ID;
  124.     }
  125.  
  126.     int GetDocumentCount() const
  127.     {
  128.         return document_count_;
  129.     }
  130.  
  131.     //в первом элементе кортежа result метод возвращает все плюс-слова запроса, содержащиеся в документе
  132.     optional<tuple<vector<string>, DocumentStatus>> MatchDocument(const string& raw_query, int document_id) const
  133.     {
  134.         if (document_id == SearchServer::INVALID_DOCUMENT_ID)                           //проверка на неверный индекс документа (и как следствие переданный неверный id)
  135.             return nullopt;
  136.  
  137.         if (raw_query.empty())                                                          //проверка на пустой запрос
  138.             return nullopt;
  139.  
  140.         auto temp_query = ParseQuery(raw_query);
  141.         if (!temp_query)
  142.             return nullopt;
  143.  
  144.         vector<string> temp_string_res;
  145.  
  146.         if (temp_query->plus_words.empty() && !temp_query->minus_words.empty())         //проверка на ситуацию "в запросе только минус слова", это не ошибка, а возврат пустого результата
  147.             return make_tuple(temp_string_res, docs_rat_n_stat_.at(document_id).doc_status);
  148.  
  149.         for (const auto& minus_word : temp_query->minus_words)                          //проверка на "если в запросе есть минус-слова то возвращаем пустой запрос" это не ошибка, а возврат пустого результата
  150.             if (d_documents_.count(minus_word) != 0)
  151.                 if (d_documents_.at(minus_word).count(document_id))
  152.                     return make_tuple(temp_string_res, docs_rat_n_stat_.at(document_id).doc_status);
  153.  
  154.         for (const auto& plus_word : temp_query->plus_words)
  155.             if (d_documents_.count(plus_word))
  156.                 if (d_documents_.at(plus_word).count(document_id))
  157.                     temp_string_res.push_back(plus_word);
  158.  
  159.         sort(temp_string_res.begin(), temp_string_res.end());
  160.  
  161.         return make_tuple(temp_string_res, docs_rat_n_stat_.at(document_id).doc_status);
  162.     }
  163.  
  164.     void SetStopWords(const string& text)
  165.     {
  166.         for (const string& word : SplitIntoWords(text))
  167.             stop_words_.insert(word);
  168.     }
  169.  
  170.     [[nodiscard]] bool AddDocument(int document_id, const string& document, DocumentStatus status, const vector<int>& ratings)
  171.     {
  172.         if (document_id < 0 || docs_rat_n_stat_.count(document_id)) //проверка на отрицательный id и повторно введенный id
  173.             return false;
  174.  
  175.         const vector<string> words = SplitIntoWordsNoStop(document);
  176.  
  177.         for (const auto& it : words)
  178.         {
  179.             if (IsValidWord(it))    //проверка на спецсимвол в запросе
  180.                 return false;
  181.  
  182.             if (d_documents_[it][document_id] > 0.0f) //если в словаре слово встречается в документе не первый раз
  183.             {
  184.                 d_documents_[it][document_id] += 1.0f / words.size();
  185.             }
  186.             else                    //если слово встретили впервые, то создаем такую запись
  187.             {
  188.                 d_documents_[it][document_id] = 1.0f / words.size();
  189.             }
  190.         }
  191.  
  192.         id_to_docnumber_[document_count_] = document_id;
  193.  
  194.         document_count_++;
  195.  
  196.         docs_rat_n_stat_.insert({ document_id, { ComputeAverageRating(ratings), status } });
  197.  
  198.         return true;
  199.     }
  200.  
  201.     optional<vector<Document>> FindTopDocuments(const string& raw_query) const
  202.     {
  203.         //vector<Document> matched_documents;
  204.         DocumentStatus status = DocumentStatus::ACTUAL;
  205.         return this->FindTopDocuments(raw_query, [status](int document_id, DocumentStatus status_to_compare, int rating) { return status_to_compare == status;});
  206.     }
  207.  
  208.     optional<vector<Document>> FindTopDocuments(const string& raw_query, DocumentStatus status) const
  209.     {
  210.         //vector<Document> matched_documents;
  211.  
  212.         return this->FindTopDocuments(raw_query, [status](int document_id, DocumentStatus status_to_compare, int rating) { return status_to_compare == status;});;
  213.     }
  214.  
  215.     template <typename T>
  216.     optional<vector<Document>> FindTopDocuments(const string& raw_query, T filter_function) const
  217.     {
  218.         if (raw_query.empty())              //проверка на пустой запрос
  219.             return nullopt;
  220.  
  221.         auto temp_query = ParseQuery(raw_query);
  222.         if (!temp_query)
  223.             return nullopt;
  224.  
  225.         //Query query;
  226.         //if (!ParseQuery(raw_query, query))  //проверка запроса на все дополнительные условия: отсутствие двойного "--", отсутствие текста после знака минус "- ", отсусттвие спецсимволов
  227.         //  return nullopt;
  228.  
  229.         auto result = FindAllDocuments(temp_query, filter_function);
  230.         if (!result)
  231.             return nullopt;
  232.  
  233.         const double EPSILON = 1e-6; //для расчета относительной погрешности и уточнении порядка сортировки
  234.  
  235.         sort(result->begin(), result->end(),
  236.             [&EPSILON](const Document& lhs, const Document& rhs) {
  237.                 return (abs(lhs.relevance - rhs.relevance) < EPSILON) ? lhs.rating > rhs.rating:lhs.relevance > rhs.relevance;
  238.             });
  239.  
  240.         //обрезаем количество выводимых документов в соответствии с заданным параметром MAX_RESULT_DOCUMENT_COUNT
  241.         if (result->size() > MAX_RESULT_DOCUMENT_COUNT)
  242.             result->resize(MAX_RESULT_DOCUMENT_COUNT);
  243.  
  244.         return result;
  245.     }
  246.  
  247. private:
  248.     struct QueryWord
  249.     {
  250.         string data;
  251.         bool is_minus;
  252.         bool is_stop;
  253.     };
  254.  
  255.     struct Query
  256.     {
  257.         set<string> plus_words;
  258.         set<string> minus_words;
  259.     };
  260.  
  261.     struct Docs_Rating_n_Status
  262.     {
  263.         int doc_rating;
  264.         DocumentStatus doc_status;
  265.     };
  266.  
  267.     map<int, Docs_Rating_n_Status> docs_rat_n_stat_;    //контейнер для хранения id документа, структуры с рейтингом и статусом документа
  268.     map<string, map<int, double>> d_documents_;         //новый контейнер для хранения инвертированного индекса
  269.     set<string> stop_words_;
  270.     int document_count_ = 0;                            //новая переменная для хранения общего количества документов
  271.  
  272.     map<int, int> id_to_docnumber_;                     //контейнер для хранения соответствий id и порядкового номера документа
  273.  
  274.     bool IsStopWord(const string& word) const
  275.     {
  276.         return stop_words_.count(word) > 0;
  277.     }
  278.  
  279.     vector<string> SplitIntoWordsNoStop(const string& text) const
  280.     {
  281.         vector<string> words;
  282.         for (const string& word : SplitIntoWords(text)) {
  283.             if (!IsStopWord(word)) {
  284.                 words.push_back(word);
  285.             }
  286.         }
  287.         return words;
  288.     }
  289.  
  290.  
  291.     static bool IsValidWord(const string& word)
  292.     {
  293.         return count_if(word.begin(), word.end(), [](char ch) {return static_cast<int>(ch) >= 0 && static_cast<int>(ch) <= 31;});
  294.  
  295.         //алтернативный вариант через none_of
  296.         /*return none_of(word.begin(), word.end(), [](char c) {
  297.             return c >= '\0' && c < ' ';
  298.             });*/
  299.     }
  300.  
  301.  
  302.     optional<QueryWord> ParseQueryWord(string text) const
  303.     {
  304.         bool is_minus = false;
  305.  
  306.         if (IsValidWord(text))
  307.             return nullopt;
  308.  
  309.         if (text == "-"s)
  310.             return nullopt;
  311.  
  312.         if (text[0] == '-')
  313.         {
  314.             if (text[1] == '-' || text[1] == ' ') // проверка на ошибки в поисковом запросе типа двойного "--" и отсутствие текста после знака - "- "
  315.                 return nullopt;
  316.  
  317.             is_minus = true;
  318.             text = text.substr(1);
  319.         }
  320.  
  321.         return QueryWord{ text, is_minus, IsStopWord(text) };
  322.     }
  323.  
  324.     optional<Query> ParseQuery(const string& text) const
  325.     {
  326.         Query temp_query;
  327.  
  328.         if (SplitIntoWords(text).empty()) //проверка на пустой запрос
  329.             return nullopt;
  330.  
  331.         for (const string& word : SplitIntoWords(text))
  332.         {
  333.  
  334.             auto query_word = ParseQueryWord(word);
  335.             if (!query_word)    //проверка слова на "минусовость" и ошибки в поисковом запросе "--", "- ", наличие спецсимволов
  336.                 return nullopt;
  337.  
  338.             if (!query_word.value().is_stop)
  339.             {
  340.                 if (query_word->is_minus)
  341.                     temp_query.minus_words.insert(query_word->data);
  342.                 else
  343.                     temp_query.plus_words.insert(query_word->data);
  344.             }
  345.         }
  346.         return temp_query;
  347.     }
  348.  
  349.     template <typename T>
  350.     optional<vector<Document>> FindAllDocuments(const optional<Query>& query, T filter_function) const
  351.     {
  352.         map<int, double> relevance_table;
  353.  
  354.         for (auto& query_word : query->plus_words)                                                          //проходим по плюс словам
  355.         {
  356.             if (d_documents_.count(query_word))                                                             //если в контейнере с обратным индексом есть плюс-слово
  357.             {
  358.                 const double d = log(document_count_ / static_cast<double>(d_documents_.at(query_word).size()));            //считаем IDF слова
  359.                 for (const auto& [id, TF] : d_documents_.at(query_word))                                    //проходим по ключу в контейнере индекса по всем документам в которых это слово есть
  360.                 {
  361.                     const auto& ref = docs_rat_n_stat_.at(id);
  362.                     if (filter_function(id, ref.doc_status, ref.doc_rating))                                //если итерируемый id документа имеет условие, которое нам было передано в FindTopDocuments
  363.                         relevance_table[id] += d * TF;                                                      //считаем IDF-TF, заносим в таблицу релевантности
  364.  
  365.                     //через распаковку будет выглядеть следующим образом:
  366.                     //  const auto& [status, rating] = docs_rat_n_stat_.at(id);
  367.                     //  if (filter_function(id, status, rating))
  368.                 }
  369.             }
  370.         }
  371.  
  372.         //проверка на минус-слова, исключение из matched_documents записей с номерами id, которые соответсвуют минус-словам в d_documents_
  373.         for (const auto& minus_word : query->minus_words)
  374.             if (d_documents_.count(minus_word) != 0)
  375.                 for (const auto& [id, TF] : d_documents_.at(minus_word))
  376.                     relevance_table.erase(id);
  377.  
  378.         vector<Document> result;
  379.         for (const auto& [index, relevance] : relevance_table)
  380.             result.push_back({ index,relevance,docs_rat_n_stat_.at(index).doc_rating });
  381.  
  382.         return result;
  383.     }
  384.  
  385.     static int ComputeAverageRating(const vector<int>& ratings)
  386.     {
  387.         if (ratings.empty())
  388.             return 0;
  389.  
  390.         int size_of_ratings = ratings.size();
  391.         int sum_id_rating = accumulate(ratings.begin(), ratings.end(), 0);
  392.  
  393.         return sum_id_rating / size_of_ratings;
  394.     }
  395. };
  396.  
  397.  
  398. //версии int main() и SearchServer CreateSearchServer() для тестирования результатов
  399.  
  400. SearchServer CreateSearchServer() {
  401.  
  402.     SearchServer search_server("и в на"s);
  403.  
  404.    
  405.     (void)search_server.AddDocument(1, "пушистый кот пушистый хвост"s, DocumentStatus::ACTUAL, { 7, 2, 7 });
  406.     if (!search_server.AddDocument(1, "пушистый пёс и модный ошейник"s, DocumentStatus::ACTUAL, { 1, 2 })) {
  407.         cout << "Документ не был добавлен, так как его id совпадает с уже имеющимся"s << endl;
  408.     }
  409.     if (!search_server.AddDocument(-1, "пушистый пёс и модный ошейник"s, DocumentStatus::ACTUAL, { 1, 2 })) {
  410.         cout << "Документ не был добавлен, так как его id отрицательный"s << endl;
  411.     }
  412.     if (!search_server.AddDocument(3, "большой пёс скво\x12рец"s, DocumentStatus::ACTUAL, { 1, 3, 2 })) {
  413.         cout << "Документ не был добавлен, так как содержит спецсимволы"s << endl;
  414.     }
  415.  
  416.     return search_server;
  417. }
  418.  
  419. void PrintDocument(const Document& docs_to_print)
  420. {
  421.     cout << "{ document_id = "s << docs_to_print.id << ", "s
  422.         << "relevance = "s << docs_to_print.relevance << ", "s
  423.         << "rating = "s << docs_to_print.rating << " }"s << endl;
  424. }
  425.  
  426.  
  427. int main() {
  428.     system("chcp 1251");
  429.  
  430.     const SearchServer search_server = CreateSearchServer();
  431.  
  432.     vector<Document> documents;
  433.  
  434.     if (search_server.FindTopDocuments("-кот"s, documents)) {
  435.         for (const Document& document : documents) {
  436.             PrintDocument(document);
  437.         }
  438.     }
  439.     else {
  440.         cout << "Ошибка в поисковом запросе"s << endl;
  441.     }
  442.  
  443.  
  444.     //tuple<vector<string>, DocumentStatus> test_tuple;
  445.     if (const auto test_tuple = search_server.MatchDocument(" - "s, search_server.GetDocumentId(-2)))
  446.     {
  447.         //for(size_t it = 0; it < test_tuple.value(vector<string>); ++it)
  448.         for (auto& it : test_tuple->_Myfirst._Val)
  449.         {
  450.             cout << it << " ";
  451.         }
  452.     }
  453.     else
  454.     {
  455.         cout << "Ошибка в поисковом запросе типа TUPLE"s << endl;
  456.     }
  457. }
Advertisement
Add Comment
Please, Sign In to add comment