C/C++

C++ задача по контейнерам

Функция FindDocuments теперь должна выглядеть так:
vector<pair<int, int>> FindDocuments(
const map<string, set<int>>& word_to_documents,
const set<string>& stop_words,
const string& query);
Создайте map<int, int> document_to_relevance для хранения релевантности по идентификатору документа.
код:

using namespace std;

string ReadLine() {
string s;
getline(cin, s);
return s;
}

int ReadLineWithNumber() {
int result;
cin >> result;
ReadLine();
return result;
}

vector<string> SplitIntoWords(const string& text) {
vector<string> words;
string word;
for (const char c : text) {
if (c == ' ') {
words.push_back(word);
word = "";
} else {
word += c;
}
}
words.push_back(word);

return words;
}

set<string> ParseStopWords(const string& text) {
set<string> stop_words;
for (const string& word : SplitIntoWords(text)) {
stop_words.insert(word);
}
return stop_words;
}

vector<string> SplitIntoWordsNoStop(const string& text, const set<string>& stop_words) {
vector<string> words;
for (const string& word : SplitIntoWords(text)) {
if (stop_words.count(word) == 0) {
words.push_back(word);
}
}
return words;
}

void AddDocument(map<string, set<int>>& word_to_documents,
const set<string>& stop_words,
int document_id,
const string& document) {
for (const string& word : SplitIntoWordsNoStop(document, stop_words)) {
word_to_documents[word].insert(document_id);
}
}

vector<int> FindDocuments(const map<string, set<int>>& word_to_documents,
const set<string>& stop_words,
const string& query) {
const vector<string> query_words = SplitIntoWordsNoStop(query, stop_words);
set<int> document_ids;
for (const string& word : query_words) {
if (word_to_documents.count(word) == 0) {
continue;
}
for (const int document_id : word_to_documents.at(word)) {
document_ids.insert(document_id);
}
// А лучше так:
// const auto& local_document_ids = word_to_documents.at(word);
// document_ids.insert(local_document_ids.begin(), local_document_ids.end());
}

return {document_ids.begin(), document_ids.end()};
}


int main() {
const string stop_words_joined = ReadLine();
const set<string> stop_words = ParseStopWords(stop_words_joined);

// Read documents
map<string, set<int>> word_to_documents;
const int document_count = ReadLineWithNumber();
for (int document_id = 0; document_id < document_count; ++document_id) {
AddDocument(word_to_documents, stop_words, document_id, ReadLine());
}

const string query = ReadLine();
for (auto [document_id, relevance] : FindDocuments(word_to_documents, stop_words, query)) {
cout << "{ document_id = "s << document_id << ", relevance = "s << relevance << " }"s << endl;
}
}
Когда вы в С/С++ пишете внутри функции определение переменной, то она выделяется В СТЕКЕ этой функции. И это происходит даже если эта переменная является объектом. Вот, например, ваша функция:
set ParseStopWords(const string& text) {
set stop_words;
for (const string& word : SplitIntoWords(text)) {
stop_words.insert(word);
}
return stop_words;
}
Когда вы в ней написали set stop_words; то компилятор это понял как "выдели мне из стека кусок памяти под размещение объекта типа "set". Потом, когда вы хотите выйти из функции, вы возвращаете указатель на этот кусок стека. А потом этот кусок стека затирается в следующей функции и весь ваш чудесный замысел накрывается медным тазом. В общем, в С++ так делать не надо, это вам не GoLang.

Далее, рассмотрим следующий кусок вашей чудесной программы, а именно ReadLineWithNumber(). Как по вашему, она что вообще должна делать? Я подозреваю, что это была попытка считать строку в которой есть число и какой-то мусор после него, который надо просто проехать. Так вот, использование для этих целей cin >> result весьма хреновая идея. Почему? А вы попробуйте сделать простую программу, где вы вводите какое-то число таким образом, а потом посмотрите что получится, если вы вместо числа введете какой-то мусор. Потом попробуйте сделать так, чтобы ваша программулька понимала, что у нее на входе мусор и надо с этим что-то делать.
А потом попробуйте другой подход: считываем линию при помощи getline(cin, s), затем берем строку и ищем в ней первый символ, который не является пробелом или табуляцией. Это будет начало поля. Запоминаем его и ищем конец поля - это будет либо конец строки, либо символ пробела или табуляции. Если строка не кончилась, то начало следующего поля по ссылке или указателю сохраняется для дальнейшего разбора. Поля надо выделять именно таким образом а не как у вас в SplitIntoWords. Когда у нас есть начало поля и длина можно при помощи метода assign класса string выделить поле в другую строку, а потом при помощи stol или иных функций из этого семейства преобразовать строку в число, попутно обрабатывая нештатные ситуации (пример тут: https://pastebin.com/J1eTwV9B )
Comfort Continent
Comfort Continent
9 624
Лучший ответ