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