View difference between Paste ID: 5NPdv53D and xUDrfkMV
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 <string>
7
#include <utility>
8
#include <vector>
9
#include <optional>
10
11
using namespace std;
12
13
constexpr double EPSILON = 1e-6;
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
template <typename StringContainer>
64
set<string> MakeUniqueNonEmptyStrings(const StringContainer& strings) {
65
    set<string> non_empty_strings;
66
    for (const string& str : strings) {
67
        if (!str.empty()) {
68
            non_empty_strings.insert(str);
69
        }
70
    }
71
    return non_empty_strings;
72
}
73
74
enum class DocumentStatus {
75
    ACTUAL,
76
    IRRELEVANT,
77
    BANNED,
78
    REMOVED,
79
};
80
81
class SearchServer {
82
public:
83
    
84
    template <typename StringContainer>
85
    explicit SearchServer(const StringContainer& stop_words) : stop_words_(MakeUniqueNonEmptyStrings(stop_words)){
86
        for (const auto& stop_word : stop_words_) { 
87
            if (IsValidWord(stop_word) == false) { 
88
                throw invalid_argument("Some of the stop words contain invalid characters"s); 
89
            }
90
        }
91
    }
92
93
    explicit SearchServer(const string& stop_words_text) : SearchServer(SplitIntoWords(stop_words_text)){
94
        
95
    }
96
97
    bool AddDocument(int document_id, const string& document, DocumentStatus status,
98
                     const vector<int>& ratings) {
99
        if(document_id < 0){
100
            throw invalid_argument("The document cannot have a negative ID"s);
101
        }
102
        if(documents_.count(document_id)){
103
            throw invalid_argument("ID belongs to an already added document"s);
104
        }
105
        if(IsValidWord(document)==false){
106
            throw invalid_argument("Document text contains invalid characters"s);
107
        }
108
        const vector<string> words = SplitIntoWordsNoStop(document);
109
        const double inv_word_count = 1.0 / words.size();
110
        for (const string& word : words) {
111
            word_to_document_freqs_[word][document_id] += inv_word_count;
112
        }
113
        document_ids.push_back(document_id);
114
        documents_.emplace(document_id, DocumentData{ComputeAverageRating(ratings), status});
115
        return true;
116
    }
117
118
    template <typename DocumentPredicate>
119
    vector<Document> FindTopDocuments(const string& raw_query,
120
                                      DocumentPredicate document_predicate) const {
121
        const Query query = ParseQuery(raw_query);
122
        auto matched_documents = FindAllDocuments(query, document_predicate);
123
        sort(matched_documents.begin(), matched_documents.end(),
124
                [](const Document& lhs, const Document& rhs) {
125
            if (abs(lhs.relevance - rhs.relevance) < EPSILON) {
126
                return lhs.rating > rhs.rating;
127
            } else {
128
                return lhs.relevance > rhs.relevance;
129
            }});
130
            if (matched_documents.size() > MAX_RESULT_DOCUMENT_COUNT) {
131
                matched_documents.resize(MAX_RESULT_DOCUMENT_COUNT);
132
            }
133
            return matched_documents;
134
    }
135
136
    vector<Document> FindTopDocuments(const string& raw_query, DocumentStatus status) const {
137
        return FindTopDocuments(raw_query, [status](int document_id, DocumentStatus document_status, int rating) {
138
        return document_status == status;});
139
    }
140
141
    vector<Document> FindTopDocuments(const string& raw_query) const {
142
        return FindTopDocuments(raw_query, DocumentStatus::ACTUAL);
143
    }
144
145
    int GetDocumentCount() const {
146
        return documents_.size();
147
    }
148
    
149
    int GetDocumentId(/*const*/ int index) const {
150
        return document_ids.at(index);
151
    }
152
153
    tuple<vector<string>, DocumentStatus> MatchDocument(const string& raw_query,
154
                                        int document_id) const {
155
        const Query query = ParseQuery(raw_query);
156
        vector<string> matched_words;
157
        for (const string& word : query.plus_words) {
158
            if (word_to_document_freqs_.count(word) == 0) {
159
                continue;
160
            }
161
            if (word_to_document_freqs_.at(word).count(document_id)) {
162
                matched_words.push_back(word);
163
            }
164
        }
165
        for (const string& word : query.minus_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.clear();
171
                break;
172
            }
173
        }
174
        auto result = tuple{matched_words, documents_.at(document_id).status};
175
        return result;
176
    }
177
178
private:
179
    struct DocumentData {
180
        int rating;
181
        DocumentStatus status;
182
    };
183
    
184
    vector<int> document_ids;
185
    set<string> stop_words_;
186
    map<string, map<int, double>> word_to_document_freqs_;
187
    map<int, DocumentData> documents_;
188
189
    bool IsStopWord(const string& word) const {
190
        return stop_words_.count(word) > 0;
191
    }
192
193
    vector<string> SplitIntoWordsNoStop(const string& text) const {
194
        vector<string> words;
195
        for (const string& word : SplitIntoWords(text)) {
196
            if (!IsStopWord(word)) {
197
                words.push_back(word);
198
            }
199
        }
200
        return words;
201
    }
202
203
    static int ComputeAverageRating(const vector<int>& ratings) {
204
        if (ratings.empty()) {
205
            return 0;
206
        }
207
        int rating_sum = 0;
208
        for (const int rating : ratings) {
209
            rating_sum += rating;
210
        }
211
        return rating_sum / static_cast<int>(ratings.size());
212
    }
213
214
    struct QueryWord {
215
        string data;
216
        bool is_minus;
217
        bool is_stop;
218
    };
219
220
    QueryWord ParseQueryWord(string text) const {
221
        if(text.empty() == true){
222
            throw invalid_argument("Search query is empty"s);
223
        }
224
        if (text.back() == '-') {
225
            throw invalid_argument("Missing text after minus sign in search query"s);
226
        }
227
        
228
        bool is_minus = false;
229
        QueryWord result;
230
        
231
        if (text[0] == '-') {
232
            is_minus = true;
233
            text = text.substr(1);
234
        }
235
        if(IsValidWord(text)==false){
236
            throw invalid_argument("There are invalid characters in the search query words"s);
237
        }
238
        if (text[0] == '-'){
239
            throw invalid_argument("Presence of more than one minus before stop words"s); 
240
        }
241
        result = {text, is_minus, IsStopWord(text)};
242
        return result;
243
    }
244
245
    struct Query {
246
        set<string> plus_words;
247
        set<string> minus_words;
248
    };
249
250
    Query ParseQuery(const string& text) const {
251
        Query query;
252
        for (const string& word : SplitIntoWords(text)) {
253
            const QueryWord query_word = ParseQueryWord(word);
254
            if (!query_word.is_stop) {
255
                if (query_word.is_minus) {
256
                    query.minus_words.insert(query_word.data);
257
                } else {
258
                    query.plus_words.insert(query_word.data);
259
                }
260
            }
261
        }
262
        return query;
263
    }
264
265
    // Existence required
266
    double ComputeWordInverseDocumentFreq(const string& word) const {
267
        return log(GetDocumentCount() * 1.0 / word_to_document_freqs_.at(word).size());
268
    }
269
270
    template <typename DocumentPredicate>
271
    vector<Document> FindAllDocuments(const Query& query,
272
                                      DocumentPredicate document_predicate) const {
273
        map<int, double> document_to_relevance;
274
        for (const string& word : query.plus_words) {
275
            if (word_to_document_freqs_.count(word) == 0) {
276
                continue;
277
            }
278
            const double inverse_document_freq = ComputeWordInverseDocumentFreq(word);
279
            for (const auto [document_id, term_freq] : word_to_document_freqs_.at(word)) {
280
                const auto& document_data = documents_.at(document_id);
281
                if (document_predicate(document_id, document_data.status, document_data.rating)) {
282
                    document_to_relevance[document_id] += term_freq * inverse_document_freq;
283
                }
284
            }
285
        }
286
287
        for (const string& word : query.minus_words) {
288
            if (word_to_document_freqs_.count(word) == 0) {
289
                continue;
290
            }
291
            for (const auto [document_id, _] : word_to_document_freqs_.at(word)) {
292
                document_to_relevance.erase(document_id);
293
            }
294
        }
295
296
        vector<Document> matched_documents;
297
        for (const auto [document_id, relevance] : document_to_relevance) {
298
            matched_documents.push_back(
299
                {document_id, relevance, documents_.at(document_id).rating});
300
        }
301
        return matched_documents;
302
    }
303
    
304
    static bool IsValidWord(const string& word) {
305
        return none_of(word.begin(), word.end(), [](char c) {
306
            return c >= '\0' && c <' ';});
307
    }
308
};
309
310
// ==================== для примера =========================
311
312
void PrintDocument(const Document& document) {
313
    cout << "{ "s
314
         << "document_id = "s << document.id << ", "s
315
         << "relevance = "s << document.relevance << ", "s
316
         << "rating = "s << document.rating << " }"s << endl;
317
}
318
int main() {
319
    SearchServer search_server("and in on wi\x17th at a"s);
320
 
321
    search_server.AddDocument(-1, "fluffy animals and feathered bird"s, DocumentStatus::ACTUAL, { -7, 1, 7, -1});
322
    search_server.AddDocument(1, "fluffy animals and feathered bird"s, DocumentStatus::ACTUAL, { 3, 5 });
323
    search_server.AddDocument(2, "a bird eats grain with a cheerful cat"s, DocumentStatus::ACTUAL, { 1, 2, 3 });
324
    search_server.AddDocument(3, "a bird eat\x17s grain with a cheerful dog"s, DocumentStatus::ACTUAL, { -1, -2, -3 });
325
    search_server.AddDocument(4, "bird and dog are good animals"s, DocumentStatus::ACTUAL, { 1, 1, 1 });
326
 
327
    search_server.FindTopDocuments("fluffy -animals"s);
328
    search_server.FindTopDocuments("--fluffy animals"s);
329
    search_server.FindTopDocuments("animals -"s);
330
    search_server.FindTopDocuments("-"s);
331
 
332
    search_server.MatchDocument("good animals"s, 4);
333
    search_server.MatchDocument("fluffy --animals"s, 1);
334
    search_server.MatchDocument("-feathered bird"s, 1);
335
    search_server.MatchDocument("bird dog -"s, 4);
336
    search_server.MatchDocument("bird - dog"s, 14);
337
} 
338