View difference between Paste ID: jFeYzena and DhSJdgvc
SHOW: | | - or go back to the newest paste.
1
// search_server_s1_t3_v1.cpp
2
3
#include <algorithm>
4
#include <cmath>
5
#include <iostream>
6
#include <map>
7
#include <set>
8
#include <string>
9
#include <utility>
10
#include <vector>
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
            words.push_back(word);
35
            word = "";
36
        } else {
37
            word += c;
38
        }
39
    }
40
    words.push_back(word);
41
    
42
    return words;
43
}
44
    
45
struct Document {
46
    int id;
47
    double relevance;
48
    int rating;
49
};
50
51
enum class DocumentStatus {
52
    ACTUAL,
53
    IRRELEVANT,
54
    BANNED,
55
    REMOVED,
56
};
57
58
class SearchServer {
59
public:
60
    void SetStopWords(const string& text) {
61
        for (const string& word : SplitIntoWords(text)) {
62
            stop_words_.insert(word);
63
        }
64
    }    
65
    
66
    void AddDocument(int document_id, const string& document, DocumentStatus status, const vector<int>& ratings) {
67
        const vector<string> words = SplitIntoWordsNoStop(document);
68
        const double inv_word_count = 1.0 / words.size();
69
        for (const string& word : words) {
70
            word_to_document_freqs_[word][document_id] += inv_word_count;
71
        }
72
        documents_.emplace(document_id, 
73
            DocumentData{
74
                ComputeAverageRating(ratings), 
75
                status
76
            });
77
    }
78
79
    template <typename DocumentPredicate>
80
    vector<Document> FindTopDocuments(const string& raw_query, DocumentPredicate document_predicate) const {            
81
        const Query query = ParseQuery(raw_query);
82
        auto matched_documents = FindAllDocuments(query, document_predicate);
83
        
84
        sort(matched_documents.begin(), matched_documents.end(),
85
             [](const Document& lhs, const Document& rhs) {
86
                if (abs(lhs.relevance - rhs.relevance) < 1e-6) {
87
                    return lhs.rating > rhs.rating;
88
                } else {
89
                    return lhs.relevance > rhs.relevance;
90
                }
91
             });
92
        if (matched_documents.size() > MAX_RESULT_DOCUMENT_COUNT) {
93
            matched_documents.resize(MAX_RESULT_DOCUMENT_COUNT);
94
        }
95
        return matched_documents;
96
    }
97
98
    vector<Document> FindTopDocuments(const string& raw_query) const {            
99
        return FindTopDocuments(raw_query, [](int document_id, DocumentStatus status, int rating) { return status == DocumentStatus::ACTUAL; });
100
    }
101
102
    int GetDocumentCount() const {
103
        return documents_.size();
104
    }
105
    
106
    tuple<vector<string>, DocumentStatus> MatchDocument(const string& raw_query, int document_id) const {
107
        const Query query = ParseQuery(raw_query);
108
        vector<string> matched_words;
109
        for (const string& word : query.plus_words) {
110
            if (word_to_document_freqs_.count(word) == 0) {
111
                continue;
112
            }
113
            if (word_to_document_freqs_.at(word).count(document_id)) {
114
                matched_words.push_back(word);
115
            }
116
        }
117
        for (const string& word : query.minus_words) {
118
            if (word_to_document_freqs_.count(word) == 0) {
119
                continue;
120
            }
121
            if (word_to_document_freqs_.at(word).count(document_id)) {
122
                matched_words.clear();
123
                break;
124
            }
125
        }
126
        return {matched_words, documents_.at(document_id).status};
127
    }
128
    
129
private:
130
    struct DocumentData {
131
        int rating;
132
        DocumentStatus status;
133
    };
134
135
    set<string> stop_words_;
136
    map<string, map<int, double>> word_to_document_freqs_;
137
    map<int, DocumentData> documents_;
138
    
139
    bool IsStopWord(const string& word) const {
140
        return stop_words_.count(word) > 0;
141
    }
142
    
143
    vector<string> SplitIntoWordsNoStop(const string& text) const {
144
        vector<string> words;
145
        for (const string& word : SplitIntoWords(text)) {
146
            if (!IsStopWord(word)) {
147
                words.push_back(word);
148
            }
149
        }
150
        return words;
151
    }
152
    
153
    static int ComputeAverageRating(const vector<int>& ratings) {
154
        int rating_sum = 0;
155
        for (const int rating : ratings) {
156
            rating_sum += rating;
157
        }
158
        return rating_sum / static_cast<int>(ratings.size());
159
    }
160
    
161
    struct QueryWord {
162
        string data;
163
        bool is_minus;
164
        bool is_stop;
165
    };
166
    
167
    QueryWord ParseQueryWord(string text) const {
168
        bool is_minus = false;
169
        // Word shouldn't be empty
170
        if (text[0] == '-') {
171
            is_minus = true;
172
            text = text.substr(1);
173
        }
174
        return {
175
            text,
176
            is_minus,
177
            IsStopWord(text)
178
        };
179
    }
180
    
181
    struct Query {
182
        set<string> plus_words;
183
        set<string> minus_words;
184
    };
185
    
186
    Query ParseQuery(const string& text) const {
187
        Query query;
188
        for (const string& word : SplitIntoWords(text)) {
189
            const QueryWord query_word = ParseQueryWord(word);
190
            if (!query_word.is_stop) {
191
                if (query_word.is_minus) {
192
                    query.minus_words.insert(query_word.data);
193
                } else {
194
                    query.plus_words.insert(query_word.data);
195
                }
196
            }
197
        }
198
        return query;
199
    }
200
    
201
    // Existence required
202
    double ComputeWordInverseDocumentFreq(const string& word) const {
203
        return log(GetDocumentCount() * 1.0 / word_to_document_freqs_.at(word).size());
204
    }
205
206
    template <typename DocumentPredicate>
207
    vector<Document> FindAllDocuments(const Query& query, DocumentPredicate document_predicate) const {
208
        map<int, double> document_to_relevance;
209
        for (const string& word : query.plus_words) {
210
            if (word_to_document_freqs_.count(word) == 0) {
211
                continue;
212
            }
213
            const double inverse_document_freq = ComputeWordInverseDocumentFreq(word);
214
            for (const auto [document_id, term_freq] : word_to_document_freqs_.at(word)) {
215
                const auto& document_data = documents_.at(document_id);
216
                if (document_predicate(document_id, document_data.status, document_data.rating)) {
217
                    document_to_relevance[document_id] += term_freq * inverse_document_freq;
218
                }
219
            }
220
        }
221
        
222
        for (const string& word : query.minus_words) {
223
            if (word_to_document_freqs_.count(word) == 0) {
224
                continue;
225
            }
226
            for (const auto [document_id, _] : word_to_document_freqs_.at(word)) {
227
                document_to_relevance.erase(document_id);
228
            }
229
        }
230
231
        vector<Document> matched_documents;
232
        for (const auto [document_id, relevance] : document_to_relevance) {
233
            matched_documents.push_back({
234
                document_id,
235
                relevance,
236
                documents_.at(document_id).rating
237
            });
238
        }
239
        return matched_documents;
240
    }
241
};
242
243
244
// ==================== для примера =========================
245
246
247
void PrintDocument(const Document& document) {
248
    cout << "{ "s
249
         << "document_id = "s << document.id << ", "s
250
         << "relevance = "s << document.relevance << ", "s
251
         << "rating = "s << document.rating
252
         << " }"s << endl;
253
}
254
255
int main() {
256
    SearchServer search_server;
257
    search_server.SetStopWords("и в на"s);
258
259
    search_server.AddDocument(0, "белый кот и модный ошейник"s,        DocumentStatus::ACTUAL, {8, -3});
260
    search_server.AddDocument(1, "пушистый кот пушистый хвост"s,       DocumentStatus::ACTUAL, {7, 2, 7});
261
    search_server.AddDocument(2, "ухоженный пёс выразительные глаза"s, DocumentStatus::ACTUAL, {5, -12, 2, 1});
262
    search_server.AddDocument(3, "ухоженный скворец евгений"s,         DocumentStatus::BANNED, {9});
263
264
    cout << "ACTUAL by default:"s << endl;
265
    for (const Document& document : search_server.FindTopDocuments("пушистый ухоженный кот"s)) {
266
        PrintDocument(document);
267
    }
268
269
    cout << "ACTUAL:"s << endl;
270
    for (const Document& document : search_server.FindTopDocuments("пушистый ухоженный кот"s, [](int document_id, DocumentStatus status, int rating) { return status == DocumentStatus::ACTUAL; })) {
271
        PrintDocument(document);
272
    }
273
274
    cout << "Even ids:"s << endl;
275
    for (const Document& document : search_server.FindTopDocuments("пушистый ухоженный кот"s, [](int document_id, DocumentStatus status, int rating) { return document_id % 2 == 0; })) {
276
        PrintDocument(document);
277
    }
278
}
279
280
281
// ==================== для тестирования =========================
282
283
284
// может быть в сданном коде
285
SearchServer CreateSearchServer() {
286
    SearchServer search_server;
287
    search_server.SetStopWords(ReadLine());
288
289
    const int document_count = ReadLineWithNumber();
290
    for (int document_id = 0; document_id < document_count; ++document_id) {
291
        const string document = ReadLine();
292
293
        int status_raw;
294
        cin >> status_raw;
295
296
        int ratings_size;
297
        cin >> ratings_size;
298
        
299
        vector<int> ratings(ratings_size, 0);
300
        
301
        for (int& rating : ratings) {
302
            cin >> rating;
303
        }
304
        
305
        search_server.AddDocument(document_id, document, static_cast<DocumentStatus>(status_raw), ratings);
306
        ReadLine();
307
    }
308
    
309
    return search_server;
310
}
311
312
void PrintMatchDocumentResult(int document_id, const vector<string>& words, DocumentStatus status) {
313
    cout << "{ "s
314
         << "document_id = "s << document_id << ", "s
315
         << "status = "s << static_cast<int>(status) << ", "s
316
         << "words ="s;
317
    for (const string& word : words) {
318
        cout << ' ' << word;
319
    }
320
    cout << "}"s << endl;
321
}
322
323
int test_main() {
324
    const SearchServer search_server = CreateSearchServer();
325
326
    const string query = ReadLine();
327
328
    cout << "Top documents for query:"s << endl;
329
    for (const Document& document : search_server.FindTopDocuments(query)) {
330
        PrintDocument(document);
331
    }
332
333
    cout << "Documents' statuses:"s << endl;
334
    const int document_count = search_server.GetDocumentCount();
335
    for (int document_id = 0; document_id < document_count; ++document_id) {
336
        const auto [words, status] = search_server.MatchDocument(query, document_id);
337
        PrintMatchDocumentResult(document_id, words, status);
338
    }
339
340
    return 0;
341
}