View difference between Paste ID: 2SuEYQER and Nq9mX0gr
SHOW: | | - or go back to the newest paste.
1
#include <algorithm>
2
#include <cmath>
3
#include <iostream>
4
#include <map>
5
#include <set>
6
#include <stdexcept>
7
#include <string>
8
#include <utility>
9
#include <vector>
10
#include <deque>
11
12
using namespace std;
13
14
const int MAX_RESULT_DOCUMENT_COUNT = 5;
15
16
string ReadLine() {
17
    string s;
18
    getline(cin, s);
19
    return s;
20
}
21
22
int ReadLineWithNumber() {
23
    int result;
24
    cin >> result;
25
    ReadLine();
26
    return result;
27
}
28
29
vector<string> SplitIntoWords(const string& text) {
30
    vector<string> words;
31
    string word;
32
    for (const char c : text) {
33
        if (c == ' ') {
34
            if (!word.empty()) {
35
                words.push_back(word);
36
                word.clear();
37
            }
38
        } else {
39
            word += c;
40
        }
41
    }
42
    if (!word.empty()) {
43
        words.push_back(word);
44
    }
45
46
    return words;
47
}
48
49
struct Document {
50
    Document() = default;
51
52
    Document(int id, double relevance, int rating)
53
        : id(id)
54
        , relevance(relevance)
55
        , rating(rating) {
56
    }
57
58
    int id = 0;
59
    double relevance = 0.0;
60
    int rating = 0;
61
};
62
63
ostream& operator<<(ostream& out, const Document& document) {
64
    out << "{ document_id = "s << document.id 
65
        << ", relevance = "s << document.relevance 
66
        << ", rating = "s << document.rating << " }"s;
67
    return out;
68
}
69
 
70
template <typename StringContainer>
71
set<string> MakeUniqueNonEmptyStrings(const StringContainer& strings) {
72
    set<string> non_empty_strings;
73
    for (const string& str : strings) {
74
        if (!str.empty()) {
75
            non_empty_strings.insert(str);
76
        }
77
    }
78
    return non_empty_strings;
79
}
80
 
81
enum class DocumentStatus {
82
    ACTUAL,
83
    IRRELEVANT,
84
    BANNED,
85
    REMOVED,
86
};
87
88
class SearchServer {
89
public:
90
    template <typename StringContainer>
91
    explicit SearchServer(const StringContainer& stop_words)
92
        : stop_words_(MakeUniqueNonEmptyStrings(stop_words))  // Extract non-empty stop words
93
    {
94
        if (!all_of(stop_words_.begin(), stop_words_.end(), IsValidWord)) {
95
            throw invalid_argument("Some of stop words are invalid"s);
96
        }
97
    }
98
99
    explicit SearchServer(const string& stop_words_text)
100
        : SearchServer(
101
            SplitIntoWords(stop_words_text))  // Invoke delegating constructor from string container
102
    {
103
    }
104
105
    void AddDocument(int document_id, const string& document, DocumentStatus status,
106
                     const vector<int>& ratings) {
107
        if ((document_id < 0) || (documents_.count(document_id) > 0)) {
108
            throw invalid_argument("Invalid document_id"s);
109
        }
110
        const auto words = SplitIntoWordsNoStop(document);
111
112
        const double inv_word_count = 1.0 / words.size();
113
        for (const string& word : words) {
114
            word_to_document_freqs_[word][document_id] += inv_word_count;
115
        }
116
        documents_.emplace(document_id, DocumentData{ComputeAverageRating(ratings), status});
117
        document_ids_.push_back(document_id);
118
    }
119
120
    template <typename DocumentPredicate>
121
    vector<Document> FindTopDocuments(const string& raw_query,
122
                                      DocumentPredicate document_predicate) const {
123
        const auto query = ParseQuery(raw_query);
124
125
        auto matched_documents = FindAllDocuments(query, document_predicate);
126
127
        sort(matched_documents.begin(), matched_documents.end(),
128
             [](const Document& lhs, const Document& rhs) {
129
                 if (abs(lhs.relevance - rhs.relevance) < 1e-6) {
130
                     return lhs.rating > rhs.rating;
131
                 } else {
132
                     return lhs.relevance > rhs.relevance;
133
                 }
134
             });
135
        if (matched_documents.size() > MAX_RESULT_DOCUMENT_COUNT) {
136
            matched_documents.resize(MAX_RESULT_DOCUMENT_COUNT);
137
        }
138
139
        return matched_documents;
140
    }
141
142
    vector<Document> FindTopDocuments(const string& raw_query, DocumentStatus status) const {
143
        return FindTopDocuments(
144
            raw_query, [status](int document_id, DocumentStatus document_status, int rating) {
145
                return document_status == status;
146
            });
147
    }
148
149
    vector<Document> FindTopDocuments(const string& raw_query) const {
150
        return FindTopDocuments(raw_query, DocumentStatus::ACTUAL);
151
    }
152
153
    int GetDocumentCount() const {
154
        return documents_.size();
155
    }
156
157
    int GetDocumentId(int index) const {
158
        return document_ids_.at(index);
159
    }
160
161
    tuple<vector<string>, DocumentStatus> MatchDocument(const string& raw_query,
162
                                                        int document_id) const {
163
        const auto query = ParseQuery(raw_query);
164
165
        vector<string> matched_words;
166
        for (const string& word : query.plus_words) {
167
            if (word_to_document_freqs_.count(word) == 0) {
168
                continue;
169
            }
170
            if (word_to_document_freqs_.at(word).count(document_id)) {
171
                matched_words.push_back(word);
172
            }
173
        }
174
        for (const string& word : query.minus_words) {
175
            if (word_to_document_freqs_.count(word) == 0) {
176
                continue;
177
            }
178
            if (word_to_document_freqs_.at(word).count(document_id)) {
179
                matched_words.clear();
180
                break;
181
            }
182
        }
183
        return {matched_words, documents_.at(document_id).status};
184
    }
185
186
private:
187
    struct DocumentData {
188
        int rating;
189
        DocumentStatus status;
190
    };
191
    const set<string> stop_words_;
192
    map<string, map<int, double>> word_to_document_freqs_;
193
    map<int, DocumentData> documents_;
194
    vector<int> document_ids_;
195
196
    bool IsStopWord(const string& word) const {
197
        return stop_words_.count(word) > 0;
198
    }
199
200
    static bool IsValidWord(const string& word) {
201
        // A valid word must not contain special characters
202
        return none_of(word.begin(), word.end(), [](char c) {
203
            return c >= '\0' && c < ' ';
204
        });
205
    }
206
207
    vector<string> SplitIntoWordsNoStop(const string& text) const {
208
        vector<string> words;
209
        for (const string& word : SplitIntoWords(text)) {
210
            if (!IsValidWord(word)) {
211
                throw invalid_argument("Word "s + word + " is invalid"s);
212
            }
213
            if (!IsStopWord(word)) {
214
                words.push_back(word);
215
            }
216
        }
217
        return words;
218
    }
219
220
    static int ComputeAverageRating(const vector<int>& ratings) {
221
        if (ratings.empty()) {
222
            return 0;
223
        }
224
        int rating_sum = 0;
225
        for (const int rating : ratings) {
226
            rating_sum += rating;
227
        }
228
        return rating_sum / static_cast<int>(ratings.size());
229
    }
230
231
    struct QueryWord {
232
        string data;
233
        bool is_minus;
234
        bool is_stop;
235
    };
236
237
    QueryWord ParseQueryWord(const string& text) const {
238
        if (text.empty()) {
239
            throw invalid_argument("Query word is empty"s);
240
        }
241
        string word = text;
242
        bool is_minus = false;
243
        if (word[0] == '-') {
244
            is_minus = true;
245
            word = word.substr(1);
246
        }
247
        if (word.empty() || word[0] == '-' || !IsValidWord(word)) {
248
            throw invalid_argument("Query word "s + text + " is invalid");
249
        }
250
251
        return {word, is_minus, IsStopWord(word)};
252
    }
253
254
    struct Query {
255
        set<string> plus_words;
256
        set<string> minus_words;
257
    };
258
259
    Query ParseQuery(const string& text) const {
260
        Query result;
261
        for (const string& word : SplitIntoWords(text)) {
262
            const auto query_word = ParseQueryWord(word);
263
            if (!query_word.is_stop) {
264
                if (query_word.is_minus) {
265
                    result.minus_words.insert(query_word.data);
266
                } else {
267
                    result.plus_words.insert(query_word.data);
268
                }
269
            }
270
        }
271
        return result;
272
    }
273
274
    // Existence required
275
    double ComputeWordInverseDocumentFreq(const string& word) const {
276
        return log(GetDocumentCount() * 1.0 / word_to_document_freqs_.at(word).size());
277
    }
278
279
    template <typename DocumentPredicate>
280
    vector<Document> FindAllDocuments(const Query& query,
281
                                      DocumentPredicate document_predicate) const {
282
        map<int, double> document_to_relevance;
283
        for (const string& word : query.plus_words) {
284
            if (word_to_document_freqs_.count(word) == 0) {
285
                continue;
286
            }
287
            const double inverse_document_freq = ComputeWordInverseDocumentFreq(word);
288
            for (const auto [document_id, term_freq] : word_to_document_freqs_.at(word)) {
289
                const auto& document_data = documents_.at(document_id);
290
                if (document_predicate(document_id, document_data.status, document_data.rating)) {
291
                    document_to_relevance[document_id] += term_freq * inverse_document_freq;
292
                }
293
            }
294
        }
295
296
        for (const string& word : query.minus_words) {
297
            if (word_to_document_freqs_.count(word) == 0) {
298
                continue;
299
            }
300
            for (const auto [document_id, _] : word_to_document_freqs_.at(word)) {
301
                document_to_relevance.erase(document_id);
302
            }
303
        }
304
305
        vector<Document> matched_documents;
306
        for (const auto [document_id, relevance] : document_to_relevance) {
307
            matched_documents.push_back(
308
                {document_id, relevance, documents_.at(document_id).rating});
309
        }
310
        return matched_documents;
311
    }
312
};
313
314
template <typename IteratorRanges>
315
class IteratorRange {
316
public:
317
    explicit IteratorRange(IteratorRanges begin, IteratorRanges end)
318
        : begin_(begin), end_(end), size_(distance(begin, end)){}
319
        
320
    IteratorRanges begin() const  {
321
        return begin_;
322
    }
323
    IteratorRanges end() const {
324
        return end_;
325
    }
326
    size_t size() const {
327
        return size_;
328
    }
329
private:
330
    IteratorRanges begin_;
331
    IteratorRanges end_;
332
    size_t size_;
333
};
334
335
template <typename To_Out>
336
ostream& operator<<(ostream& out, const IteratorRange<To_Out>& sheet) {
337
    auto i = sheet.begin();
338
    while(i!= sheet.end()){
339
        out << *i;
340
        ++i;
341
    }
342
    return out;
343
}
344
  
345
template <typename Paginatorr>
346
class Paginator {
347
public :
348
    Paginator(const Paginatorr& result_begin, const Paginatorr& result_end, size_t size_of_sheet){
349
        auto full_size = distance(result_begin, result_end);
350
        Paginatorr helper = result_begin;
351
        for (auto i = 0; i < full_size/size_of_sheet; ++i) {
352
            sheets.push_back(IteratorRange<Paginatorr>(helper, helper+ size_of_sheet));
353
            helper=helper+size_of_sheet;
354
        }
355
        if (helper!= result_end) {
356
            sheets.push_back(IteratorRange<Paginatorr>(helper, result_end));
357
        }
358
    }
359
    auto begin() const {
360
        return sheets.begin();
361
    }
362
    auto end() const {
363
        return sheets.end();
364
    }
365
    size_t size() {
366
        return sheets.size();
367
    }
368
private: 
369
    vector<IteratorRange<Paginatorr>> sheets;
370
};
371
372
template <typename Container>
373
auto Paginate(const Container& c, size_t page_size) {
374
    return Paginator(begin(c), end(c), page_size);
375
}
376
377
class RequestQueue {
378
public:
379
    explicit RequestQueue(const SearchServer& search_server) : search_request(search_server){}
380
381
    template <typename DocumentPredicate>
382
    vector<Document> AddFindRequest(const string& raw_query, DocumentPredicate document_predicate) {
383
384
        vector<Document> helper = search_request.FindTopDocuments(raw_query, document_predicate);
385
        
386
        QueryResult query;
387
        query.query_result = (helper.empty()==false);
388
        
389
        if (!(requests_.size() < min_in_day_)){
390
            requests_.pop_front();
391
        } 
392
        requests_.push_back(query); 
393
        return helper;
394
    }
395
    
396
    vector<Document> AddFindRequest(const string& raw_query, DocumentStatus status) {
397
         return AddFindRequest(raw_query, [&status](int document_id, DocumentStatus document_status, int rating) {
398
            return document_status == status;});
399
    }
400
    
401
    vector<Document> AddFindRequest(const string& raw_query) {
402
        return AddFindRequest(raw_query, DocumentStatus::ACTUAL);
403
    }
404
    
405
    int GetNoResultRequests() const {
406
        return count_if(requests_.begin(), requests_.end(), [](QueryResult query) {return query.query_result == 0;});
407
    }
408
private:
409
    struct QueryResult {
410
        bool query_result;
411
    };
412
    const SearchServer& search_request;
413
    deque<QueryResult> requests_;
414
    const static int min_in_day_ = 1440;
415
}; 
416
417
int main() {
418
    SearchServer search_server("and in at"s);
419
    RequestQueue request_queue(search_server);
420
    search_server.AddDocument(1, "curly cat curly tail"s, DocumentStatus::ACTUAL, {7, 2, 7});
421
    search_server.AddDocument(2, "curly dog and fancy collar"s, DocumentStatus::ACTUAL, {1, 2, 3});
422
    search_server.AddDocument(3, "big cat fancy collar "s, DocumentStatus::ACTUAL, {1, 2, 8});
423
    search_server.AddDocument(4, "big dog sparrow Eugene"s, DocumentStatus::ACTUAL, {1, 3, 2});
424
    search_server.AddDocument(5, "big dog sparrow Vasiliy"s, DocumentStatus::ACTUAL, {1, 1, 1});
425
    // 1439 запросов с нулевым результатом
426
    for (int i = 0; i < 1439; ++i) {
427
        request_queue.AddFindRequest("empty request"s);
428
    }
429
    // все еще 1439 запросов с нулевым результатом
430
    request_queue.AddFindRequest("curly dog"s);
431
    // новые сутки, первый запрос удален, 1438 запросов с нулевым результатом
432
    request_queue.AddFindRequest("big collar"s);
433
    // первый запрос удален, 1437 запросов с нулевым результатом
434
    request_queue.AddFindRequest("sparrow"s);
435
    cout << "Total empty requests: "s << request_queue.GetNoResultRequests() << endl;
436
    return 0;
437
}