Advertisement
giGii

my_rating1

Aug 22nd, 2022
55
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 9.90 KB | None | 0 0
  1. #include <algorithm>
  2. #include <iostream>
  3. #include <set>
  4. #include <map>
  5. #include <string>
  6. #include <utility>
  7. #include <vector>
  8. #include <cmath>
  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 = 0;
  22.     cin >> result;
  23.     ReadLine();
  24.     return result;
  25. }
  26.  
  27. vector<string> SplitIntoWords(const string& text) {
  28.  
  29.     vector<string> words;
  30.     string word;
  31.     for (const char c : text) {
  32.         if (c == ' ') {
  33.             if (!word.empty()) {
  34.                 words.push_back(word);
  35.                 word.clear();
  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.     int id;
  50.     double relevance;
  51.     int rating;
  52. };
  53.  
  54. class SearchServer {
  55. public:
  56.     void SetStopWords(const string& text) {
  57.  
  58.         for (const string& word : SplitIntoWords(text)) {
  59.             stop_words_.insert(word);
  60.         }
  61.     }
  62.  
  63.     void AddDocument(int document_id, const string& document, const vector<int> ratings) {
  64.  
  65.         // наполняем счетчик документов -- он пригодится для подсчета IDF.
  66.         ++document_count_;
  67.  
  68.         const vector<string> words = SplitIntoWordsNoStop(document);
  69.        
  70.         if (ratings.size() > 0 && ratings[0] > 0) {  // второе условие на случай если не просто пуста строка а строка с 0, типа 0 человек дали оценку документу
  71.             documents_rating_[document_id] = ComputeAverageRating(ratings);
  72.         } else {
  73.             documents_rating_[document_id] = 0; // если нет оценок, по условию рэйтинг такого документа равен нулю
  74.         }
  75.  
  76.         // Рассчитываем TF каждого слова в каждом документе.
  77.         for (const string& word : words) {
  78.             TF_[word][document_id] += 1.0 / words.size();
  79.             index_[word].insert(document_id);
  80.         }
  81.     }
  82.  
  83.     vector<Document> FindTopDocuments(const string& raw_query) const {
  84.  
  85.         const PlusMinusWords query_words = ParseQuery(raw_query);
  86.  
  87.         auto matched_documents = FindAllDocuments(query_words);
  88.  
  89.         sort(matched_documents.begin(), matched_documents.end(),
  90.              [](const Document& lhs, const Document& rhs) {
  91.                  return lhs.relevance > rhs.relevance;
  92.              });
  93.         if (matched_documents.size() > MAX_RESULT_DOCUMENT_COUNT) {
  94.             matched_documents.resize(MAX_RESULT_DOCUMENT_COUNT);
  95.         }
  96.         return matched_documents;
  97.     }
  98.  
  99. private:
  100.  
  101.     struct PlusMinusWords {
  102.         set<string> plus_words;
  103.         set<string> minus_words;
  104.     };
  105.  
  106.     int document_count_ = 0;
  107.  
  108.     map<int, int> documents_rating_;
  109.  
  110.     map<string, map<int, double>> TF_;
  111.  
  112.     map<string, set<int>> index_;
  113.  
  114.     set<string> stop_words_;
  115.  
  116.     bool IsStopWord(const string& word) const {
  117.         return stop_words_.count(word) > 0;
  118.     }
  119.  
  120.     vector<string> SplitIntoWordsNoStop(const string& text) const {
  121.         vector<string> words;
  122.         for (const string& word : SplitIntoWords(text)) {
  123.             if (!IsStopWord(word)) {
  124.                 words.push_back(word);
  125.             }
  126.         }
  127.         return words;
  128.     }
  129.  
  130.     PlusMinusWords ParseQuery(const string& text) const {
  131.  
  132.         PlusMinusWords query_words;
  133.  
  134.         for (const string& word : SplitIntoWordsNoStop(text)) {
  135.             if (word[0] == '-') {
  136.                 query_words.minus_words.insert(word.substr(1, word.size()));
  137.             } else {
  138.                 if (query_words.minus_words.count(word) == 0) {
  139.                     query_words.plus_words.insert(word);
  140.                 }
  141.             }
  142.         }
  143.         return query_words;
  144.     }
  145.  
  146.     vector<Document> FindAllDocuments(const PlusMinusWords& query_words) const {
  147.  
  148.         /* Рассчитываем IDF каждого плюс-слова в запросе:
  149.         1) количество документов document_count_ делим на количество документов, где это слово встречается;
  150.         2) берем от полученного значения log.
  151.  
  152.         Функция AddDocument построила index_, где каждому слову отнесено множество документов, где оно встречается.
  153.         */
  154.  
  155.         map<string, double> idf; // в результате получим слово из запроса и его посчитанный IDF (не факт, что все слова из запроса обрели IDF, ведь слова может не быть в индексе, а значит знаменателя нет).
  156.         for (const string& word : query_words.plus_words) {
  157.             if (index_.count(word) != 0) { // если слово есть в индексе, значит можно быстро понять, в скольких документах оно есть -- index_.at(word).size().
  158.                 idf[word] = log(static_cast<double>(document_count_) / index_.at(word).size());
  159.             }
  160.         }
  161.  
  162.         map<int, double> matched_documents;
  163.  
  164.         for (const auto& [word, idf] : idf) { // раз мы идем здесь по словарю idf, значит мы идем по плюс-словам запроса.
  165.             if (TF_.count(word) != 0) { // если плюс-слово запроса есть в TF_, значит по TF_.at(плюс-слово запроса) мы получим все id документов, где это слово имеет вес tf, эти документы интересы.
  166.                 for (const auto& [document_id, tf] : TF_.at(word)) { // будем идти по предпосчитанному TF_.at(плюс-слово запроса) и наращивать релевантность документам по их id по офрмуле IDF-TF.
  167.                     matched_documents[document_id] += idf * tf;
  168.                 }
  169.             }
  170.         }
  171.  
  172.         /* теперь надо пройтись по минус словам и посмотреть при помощи index_,
  173.         какие id документов есть по этому слову, и вычеркнуть их из выдачи.
  174.         */
  175.  
  176.         for (const string& word : query_words.minus_words) {
  177.             if (index_.count(word) != 0) {
  178.                 for (const auto& documents_id : index_.at(word)) {
  179.                     if (matched_documents.count(documents_id) != 0) {
  180.                         matched_documents.erase(documents_id);
  181.                     }
  182.  
  183.                 }
  184.             }
  185.         }
  186.  
  187.         vector<Document> result;
  188.  
  189.         for (const auto& [document_id, relevance] : matched_documents) {
  190.             result.push_back({document_id, relevance, documents_rating_.at(document_id)});
  191.         }
  192.  
  193.         return result;
  194.     }
  195.  
  196.     int ComputeAverageRating(const vector<int>& ratings) {
  197.         int sum_rating = 0;
  198.         for (int i = 1; i <= ratings[0]; ++i) {
  199.             sum_rating += ratings[i];
  200.         }
  201.  
  202.         return sum_rating / ratings[0];
  203.     }
  204. };
  205.  
  206. SearchServer CreateSearchServer() {
  207.  
  208.     SearchServer search_server;
  209.     search_server.SetStopWords(ReadLine());
  210.  
  211.     const int document_count = ReadLineWithNumber();
  212.     for (int document_id = 0; document_id < document_count; ++document_id) {
  213.         string document = ReadLine();
  214.  
  215.         vector<string> ratings_line = SplitIntoWords(ReadLine());
  216.         vector<int> ratings;
  217.  
  218.         if (ratings_line.size() > 0 && stoi(ratings_line[0]) > 0) {
  219.             for (string num : ratings_line) {
  220.                 ratings.push_back(stoi(num));
  221.             }
  222.         }
  223.         search_server.AddDocument(document_id, document, ratings);
  224.     }
  225.  
  226.     return search_server;
  227. }
  228.  
  229. int main() {
  230.  
  231.     const SearchServer search_server = CreateSearchServer();
  232.  
  233.     const string query = ReadLine();
  234.  
  235.     for (const auto& [document_id, relevance, rating] : search_server.FindTopDocuments(query)) {
  236.         cout <<
  237.         "{ document_id = "s << document_id << ", "
  238.         << "relevance = "s << relevance << ", "s
  239.         << "rating = "s << rating << " }" << endl;
  240.     }
  241. }
  242. /*
  243.  
  244. тест1 -- обычный --- OK:
  245.  
  246. i v na
  247. 3
  248. belyj kot i modnyj oshejnik
  249. 2 8 -3
  250. pushistyj kot pushistyj hvost
  251. 3 7 2 7
  252. uhozhennyj pes vyrazitelnye glaza
  253. 4 5 -12 2 1
  254. pushistyj uhozhennyj kot
  255.  
  256. вывод:
  257. { document_id = 1, relevance = 0.650672, rating = 5 }
  258. { document_id = 2, relevance = 0.274653, rating = -1 }
  259. { document_id = 0, relevance = 0.101366, rating = 2 }
  260.  
  261. тест2 -- с минус-словом "кот", которое должно вычеркнуть 0 и 1 документы --- OK:
  262.  
  263. i v na
  264. 3
  265. belyj kot i modnyj oshejnik
  266. 2 8 -3
  267. pushistyj kot pushistyj hvost
  268. 3 7 2 7
  269. uhozhennyj pes vyrazitelnye glaza
  270. 4 5 -12 2 1
  271. pushistyj uhozhennyj -kot
  272.  
  273. вывод:
  274. { document_id = 2, relevance = 0.274653, rating = -1 }
  275.  
  276. тест3 -- без стоп-слов, релевантность нулевого документа должна измениться,
  277. т.к. "kot" делится на большее количество слов; также есть нулевой рейтинг --- OK:
  278.  
  279. // пустую строку над тройкой тоже надо копировать, она символизирует отсутсвтие стоп-слов
  280.  
  281. 3
  282. belyj kot i modnyj oshejnik
  283. 2 8 -3
  284. pushistyj kot pushistyj hvost
  285.  
  286. uhozhennyj pes vyrazitelnye glaza
  287. 4 5 -12 2 1
  288. pushistyj uhozhennyj kot
  289.  
  290. вывод:
  291. { document_id = 1, relevance = 0.650672, rating = 0 }
  292. { document_id = 2, relevance = 0.274653, rating = -1 }
  293. { document_id = 0, relevance = 0.081093, rating = 2 }
  294.  
  295. */
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement