Один из возможных подходов:
Вместо unordered_map
используйте std::vector<std::pair<std::string, int>>
, который будет отсортирован по строке. При сканировании всех подстрок каждой строки, которую вы читаете, ищите их с помощью двоичного поиска (std::lower_bound()
полезно), вставляя или обновляя соответствующим образом (если это небольшой фиксированный алфавит, такой как ДНК, вы можете даже сгенерировать всю длину - K
подстроки опережают время и предварительно заполняют вектор, чтобы избежать вставки позже).
Когда закончите, пересортируйте вектор на основе отсчетов в порядке убывания ... std::partial_sort()
было бы действительно удобно, так как вам нужны только первые N
элементы:
std::partial_sort(words.begin(), words.begin() + N, words.end(),
[](const auto &a, const auto &b){ return a.second > b.second; });
По сути, что-то вроде:
#include <string>
#include <string_view>
#include <iostream>
#include <algorithm>
#include <vector>
#include <utility>
#include <cstdlib>
constexpr std::size_t K = 4;
constexpr std::size_t N = 2;
int main() {
std::vector<std::pair<std::string, int>> words;
std::string line;
while (std::getline(std::cin, line)) {
auto len = line.size();
for (auto i = 0U; i < len - K + 1; i += 1) {
auto word = std::string_view(line.c_str() + i, K);
auto pos = std::lower_bound(words.begin(), words.end(), word,
[](const auto &a, const auto &b){
return a.first < b;
});
if (pos == words.end() || pos->first != word) {
words.emplace(pos, std::string(word), 1);
} else {
pos->second += 1;
}
}
}
auto sort_to = std::min(words.size(), N);
std::partial_sort(words.begin(), words.begin() + sort_to, words.end(),
[](const auto &a, const auto &b){
return a.second > b.second;
});
for (auto i = 0U; i < sort_to; i += 1) {
std::cout << words[i].first << " - " << words[i].second << " occurences\n";
}
return 0;
}