Advertisement
VinnRonn

search_server

Jul 10th, 2022 (edited)
41
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 15.50 KB | None | 0 0
  1. #include <algorithm>
  2. #include <cassert>
  3. #include <iostream>
  4. #include <set>
  5. #include <map>
  6. #include <string>
  7. #include <utility>
  8. #include <vector>
  9. #include <cmath>
  10. #include <numeric>
  11.  
  12. using namespace std;
  13.  
  14. const int MAX_RESULT_DOCUMENT_COUNT = 5;
  15.  
  16. const double EPSILON = 1e-6;
  17.  
  18. template<typename T, typename U>
  19. ostream& operator<<(ostream& out, const pair<T, U>& v) {
  20. out << v.first << ": " << v.second;
  21. return out;
  22. }
  23.  
  24. template<typename Element>
  25. void Print(ostream& out, const Element& container) {
  26. bool is_first = true;
  27. for (const auto& element : container) {
  28. if (!is_first) {
  29. out << ", "s;
  30. }
  31. is_first = false;
  32. out << element;
  33. }
  34. }
  35.  
  36. template<typename T>
  37. ostream& operator<<(ostream& out, const vector<T>& v) {
  38. out << "[";
  39. Print(out, v);
  40. out << "]";
  41. return out;
  42. }
  43.  
  44. template<typename T>
  45. ostream& operator<<(ostream& out, const set<T>& v) {
  46. out << "{";
  47. Print(out, v);
  48. out << "}";
  49. return out;
  50. }
  51.  
  52.  
  53. template<typename T, typename U>
  54. ostream& operator<<(ostream& out, const map<T, U>& m) {
  55. out << "{";
  56. Print(out, m);
  57. out << "}";
  58. return out;
  59. }
  60.  
  61.  
  62. string ReadLine() {
  63. string s;
  64. getline(cin, s);
  65. return s;
  66. }
  67.  
  68. int ReadLineWithNumber() {
  69. int result = 0;
  70. cin >> result;
  71. ReadLine();
  72. return result;
  73. }
  74.  
  75. // type of doc
  76. enum class DocumentStatus {
  77. ACTUAL,
  78. IRRELEVANT,
  79. BANNED,
  80. REMOVED
  81. };
  82.  
  83. vector<string> SplitIntoWords(const string& text) {
  84. vector<string> words;
  85. string word;
  86. for (const char c : text) {
  87. if (c == ' ') {
  88. if (!word.empty()) {
  89. words.push_back(word);
  90. word.clear();
  91. }
  92. }
  93. else {
  94. word += c;
  95. }
  96. }
  97. if (!word.empty()) {
  98. words.push_back(word);
  99. }
  100.  
  101. return words;
  102. }
  103.  
  104. struct Document {
  105. Document() = default;
  106. Document(int id1, double relevance1, int rating1) :
  107. id(id1),
  108. relevance(relevance1),
  109. rating(rating1) {}
  110.  
  111. int id = 0;
  112. double relevance = 0.0;
  113. int rating = 0;
  114. };
  115.  
  116. bool FindWord(const string& word, const map<string, set<int>>& word_to_documents, const int& id) {
  117. return word_to_documents.count(word) && word_to_documents.at(word).count(id);
  118. }
  119.  
  120.  
  121. void PrintMatchDocumentResult(int document_id, const vector<string>& words, DocumentStatus status) {
  122. cout << "{ "s
  123. << "document_id = "s << document_id << ", "s
  124. << "status = "s << static_cast<int>(status) << ", "s
  125. << "words ="s;
  126. for (const string& word : words) {
  127. cout << ' ' << word;
  128. }
  129. cout << "}"s << endl;
  130. }
  131. class SearchServer {
  132. public:
  133. inline static constexpr int INVALID_DOCUMENT_ID = -1;
  134.  
  135. int GetDocumentId(int index) const {
  136.  
  137. if (index < documents_id_.size() && index >= 0) {
  138. return documents_id_[index];
  139. }
  140. return SearchServer::INVALID_DOCUMENT_ID;
  141. }
  142.  
  143. SearchServer() = default;
  144. explicit SearchServer(const string& stop_words) {
  145. SetStopWords(stop_words);
  146. }
  147.  
  148. template<typename StringCollection>
  149. SearchServer(const StringCollection& stop_words) {
  150. for (const auto& word : stop_words) {
  151. if (!word.empty()) {
  152. stop_words_.insert(word);
  153. }
  154. }
  155. }
  156.  
  157. void SetStopWords(const string& text) {
  158. for (const string& word : SplitIntoWords(text)) {
  159. stop_words_.insert(word);
  160. }
  161. }
  162. [[nodiscard]] bool AddDocument(int document_id, const string& document, DocumentStatus status, const vector<int>& ratings) {
  163.  
  164. for (const auto& w : SplitIntoWords(document)) {
  165. if (IsValidWord(w)) {
  166. continue;
  167. }
  168. else {
  169. return false;
  170. }
  171. }
  172.  
  173. const vector<string> words = SplitIntoWordsNoStop(document);
  174. double count_word = 0.0;
  175. for (const auto& w : words) {
  176.  
  177. count_word = count(words.begin(), words.end(), w);
  178. word_to_documents_[w].insert(document_id);
  179. word_to_document_freqs_[w].insert({ document_id, count_word / words.size() });
  180. }
  181. if (document_id < 0) {
  182. return false;
  183. }
  184. if (count(begin(documents_id_), end(documents_id_), document_id) > 0) {
  185. return false;
  186. }
  187.  
  188. else {
  189.  
  190. documents_id_.push_back(document_id);
  191. sort(begin(documents_id_), end(documents_id_));
  192.  
  193. document_rating_[document_id] = ComputeAverageRating(ratings);
  194.  
  195. document_status_[document_id] = status;
  196.  
  197.  
  198. }
  199. ++documents_count_;
  200.  
  201. return true;
  202. }
  203.  
  204.  
  205. template<typename DocumentPredicate>
  206. [[nodiscard]] bool FindTopDocuments(const string& raw_query, DocumentPredicate document_predicate, vector<Document>& result) const {
  207.  
  208. for (const auto& w : SplitIntoWords(raw_query)) {
  209. if (!IsValidWord(w)) {
  210. return false;
  211. }
  212. }
  213.  
  214. const Query query_words = ParseQuery(raw_query);
  215.  
  216. for (const auto& word : query_words.minus_words) {
  217.  
  218. if (word[0] == '-' || word.empty()) {
  219. return false;
  220. }
  221. }
  222.  
  223. auto matched_documents = FindAllDocuments(query_words, document_predicate);
  224.  
  225. sort(matched_documents.begin(), matched_documents.end(),
  226. [](const Document& lhs, const Document& rhs) {
  227. if (abs(lhs.relevance - rhs.relevance) < EPSILON) {
  228. return lhs.rating > rhs.rating;
  229. }
  230. else {
  231. return lhs.relevance > rhs.relevance;
  232. }
  233. });
  234.  
  235. if (matched_documents.size() > MAX_RESULT_DOCUMENT_COUNT) {
  236. matched_documents.resize(MAX_RESULT_DOCUMENT_COUNT);
  237. }
  238.  
  239. result = matched_documents;
  240.  
  241. return true;
  242. }
  243.  
  244. [[nodiscard]] bool FindTopDocuments(const string& raw_query, DocumentStatus status, vector<Document>& result) const {
  245. return FindTopDocuments(raw_query, [status](int document_id, DocumentStatus n_status, int rating)
  246. { return n_status == status; }, result);
  247. }
  248.  
  249. [[nodiscard]] bool FindTopDocuments(const string& raw_query, vector<Document>& result) const {
  250. return FindTopDocuments(raw_query, DocumentStatus::ACTUAL, result);
  251. }
  252.  
  253. static int ComputeAverageRating(const vector<int>& ratings) {
  254. if (ratings.empty()) {
  255. return 0;
  256. }
  257. int document_rating = accumulate(ratings.begin(), ratings.end(), 0);
  258. return document_rating / static_cast<int>(ratings.size());
  259. }
  260. //tuple<vector<string>, DocumentStatus> match_doc;
  261. [[nodiscard]] bool MatchDocument(const string& raw_query, int document_id, tuple<vector<string>, DocumentStatus>& result) const {
  262.  
  263. //исключаю пустой запрос и кривой id
  264. if (raw_query.empty() || document_id > documents_count_) {
  265. result = { {}, document_status_.at(document_id) };
  266. return true;
  267. }
  268.  
  269. Query query;
  270. query = ParseQuery(raw_query);
  271.  
  272. for (const auto& minus : query.minus_words) {
  273. if (!IsValidWord(minus)) {
  274. return false;
  275. }
  276.  
  277. if (FindWord(minus, word_to_documents_, document_id)) {
  278. result = { {}, document_status_.at(document_id) };
  279. return true;
  280. }
  281. }
  282.  
  283. vector<string> words;
  284.  
  285. for (const auto& plus : query.plus_words) {
  286. if (!IsValidWord(plus)) {
  287. return false;
  288. }
  289.  
  290. if (FindWord(plus, word_to_documents_, document_id)) {
  291. words.push_back(plus);
  292. }
  293. }
  294. sort(words.begin(), words.end());
  295. result = { words, document_status_.at(document_id) };
  296. return true;
  297. };
  298.  
  299. int GetDocumentCount() {
  300. return documents_count_;
  301. }
  302.  
  303. private:
  304.  
  305. struct Status { // почему не хранить статус в документе?
  306. int id;
  307. double rating;
  308. DocumentStatus status;
  309. };
  310.  
  311. struct Query {
  312.  
  313. set<string> plus_words;
  314. set<string> minus_words;
  315. };
  316.  
  317. map<string, set<int>> word_to_documents_;
  318. map<string, map<int, double>> word_to_document_freqs_;
  319.  
  320. set<string> stop_words_;
  321.  
  322. map<int, int> document_rating_;
  323. map<int, DocumentStatus> document_status_;
  324. vector<int> documents_id_;
  325.  
  326. int documents_count_ = 0;
  327.  
  328. bool IsStopWord(const string& word) const {
  329. return stop_words_.count(word) > 0;
  330. }
  331.  
  332. vector<string> SplitIntoWordsNoStop(const string& text) const {
  333. vector<string> words;
  334. vector<string> split_into_words;
  335. for (const string& word : SplitIntoWords(text)) {
  336.  
  337. if (!IsStopWord(word))
  338. {
  339. words.push_back(word);
  340. }
  341. }
  342. return words;
  343. }
  344.  
  345. Query ParseQuery(const string& text) const {
  346. Query query;
  347. vector<string> raw_words = SplitIntoWordsNoStop(text);
  348.  
  349. for (const string& word : raw_words) {
  350. if (word[0] == '-')
  351. {
  352. query.minus_words.insert(word.substr(1));
  353. }
  354. }
  355. for (const string& min : raw_words) {
  356. if (min[0] != '-' && !query.minus_words.count(min))
  357. {
  358. query.plus_words.insert(min);
  359. }
  360. }
  361. return query;
  362. }
  363.  
  364.  
  365. // IDF-TF
  366. template<typename DocumentPredicate>
  367. vector<Document> FindAllDocuments(const Query& query_words, DocumentPredicate document_predicate) const {
  368. map<int, double> document_to_relevance;
  369.  
  370. for (const auto& plus : query_words.plus_words) {
  371. if (word_to_documents_.count(plus)) {
  372. const double IDF = log(static_cast<double>(documents_count_) / word_to_documents_.at(plus).size());
  373. for (const auto& id : word_to_documents_.at(plus)) {
  374. if (document_predicate(id, document_status_.at(id), document_rating_.at(id))) {
  375. document_to_relevance[id] += IDF * word_to_document_freqs_.at(plus).at(id);
  376. }
  377. }
  378. }
  379. }
  380.  
  381. for (const auto& minus : query_words.minus_words) {
  382. if (word_to_documents_.count(minus)) {
  383. for (const auto& id : word_to_documents_.at(minus)) {
  384. document_to_relevance.erase(id);
  385. };
  386. }
  387. }
  388.  
  389. vector<Document> matched_documents;
  390. for (const auto& [id, relevance] : document_to_relevance) {
  391. matched_documents.push_back({ id, relevance, document_rating_.at(id) });
  392. }
  393. return matched_documents;
  394. }
  395.  
  396. static bool IsValidWord(const string& word) {
  397. // A valid word must not contain special characters
  398. return none_of(word.begin(), word.end(), [](char c) {
  399. return c >= '\0' && c < ' ';
  400. });
  401. }
  402.  
  403. };
  404. /*
  405. Подставьте сюда вашу реализацию макросов
  406. ASSERT, ASSERT_EQUAL, ASSERT_EQUAL_HINT, ASSERT_HINT и RUN_TEST
  407. */
  408. //template<typename T, typename U>
  409. //void AssertEqualImlp(const T& t, const U& u, const string& t_str, const string& u_str,
  410. // const string& file, const string& func, const int& line, const string& hint) {
  411. // if (t != u) {
  412. // cerr << boolalpha;
  413. // cerr << file << "("s << line << "): "s << func << ": "s;
  414. // cerr << "ASSERT_EQUAL("s << t_str << ", "s << u_str << ") failed: "s;
  415. // cerr << t << "!=" << u << "."s;
  416. // if (!hint.empty()) {
  417. // cerr << "Hint: "s << hint;
  418. // }
  419. // cerr << endl;
  420. // abort();
  421. // }
  422. //}
  423. //
  424. //#define ASSERT_EQUAL(a, b) AssertEqualImlp((a), (b), #a, #b, __FILE__, __FUNCTION__, __LINE__, ""s)
  425. //#define ASSERT_EQUAL_HINT(a, b) AssertEqualImlp((a), (b), #a, #b, __FILE__, __FUNCTION__, __LINE__, (hint))
  426. //
  427. //
  428. //void AssertImlp(bool value, const string& expr_str,
  429. // const string& file,
  430. // const string& func,
  431. // const int& line,
  432. // const string& hint) {
  433. // if (!value) {
  434. // cerr << file << ": "s << "("s << line << ") "s << func << ": "s;
  435. // cerr << "ASSERT"s << expr_str << ", "s << ") failed: "s << value;
  436. // if (!hint.empty()) {
  437. // cerr << "Hint: "s << hint;
  438. // }
  439. // cerr << endl;
  440. // abort();
  441. // }
  442. //}
  443. //
  444. //#define ASSERT(expr) AssertImlp((expr), #expr, __FILE__, __FUNCTION__, __LINE__, ""s)
  445. //#define ASSERT_HINT(expr) AssertImlp((expr), #expr, __FILE__, __FUNCTION__, __LINE__, (hint))
  446.  
  447. // -------- Начало модульных тестов поисковой системы ----------
  448.  
  449. // Тест проверяет, что поисковая система исключает стоп-слова при добавлении документов
  450.  
  451. // --------- Окончание модульных тестов поисковой системы -----------
  452. void PrintDocument(const Document& document) {
  453. cout << "{ "s
  454. << "document_id = "s << document.id << ", "s
  455. << "relevance = "s << document.relevance << ", "s
  456. << "rating = "s << document.rating
  457. << " }"s << endl;
  458. }
  459.  
  460. int main() {
  461.  
  462. //TestSearchServer();
  463. // Если вы видите эту строку, значит все тесты прошли успешно
  464. //cout << "Search server testing finished"s << endl;
  465. SearchServer search_server("и в на"s);
  466.  
  467. // Явно игнорируем результат метода AddDocument, чтобы избежать предупреждения
  468. // о неиспользуемом результате его вызова
  469. (void)search_server.AddDocument(1, "пуш-истый кот пушистый хвост"s, DocumentStatus::ACTUAL, { 7, 2, 7 });
  470.  
  471. if (!search_server.AddDocument(1, "пушистый пёс и модный ошейник"s, DocumentStatus::ACTUAL, { 1, 2 })) {
  472. cout << "Документ не был добавлен, так как его id совпадает с уже имеющимся"s << endl;
  473. }
  474.  
  475. if (!search_server.AddDocument(-1, "пушистый пёс и модный ошейник"s, DocumentStatus::ACTUAL, { 1, 2 })) {
  476. cout << "Документ не был добавлен, так как его id отрицательный"s << endl;
  477. }
  478.  
  479. if (!search_server.AddDocument(3, "большой пёс скво\x12рец"s, DocumentStatus::ACTUAL, { 1, 3, 2 })) {
  480. cout << "Документ не был добавлен, так как содержит спецсимволы"s << endl;
  481. }
  482.  
  483.  
  484. vector<Document> documents;
  485. if (search_server.FindTopDocuments("--пушистый"s, documents)) {
  486. for (const Document& document : documents) {
  487. PrintDocument(document);
  488. }
  489. }
  490. else {
  491. cout << "Ошибка в поисковом запросе"s << endl;
  492. }
  493. {
  494. vector<Document> documents;
  495. if (search_server.FindTopDocuments("кот -"s, documents)) {
  496. for (const Document& document : documents) {
  497. PrintDocument(document);
  498. }
  499. }
  500. else {
  501. cout << "Ошибка в поисковом запросе11"s << endl;
  502. }
  503. }
  504.  
  505.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement