#c #string #hash #string-comparison
#c #строка #хэш #сравнение строк
Вопрос:
Если у меня есть список строк, например:
Dog
Cat
Tree
NiceCar
Car
И затем я получаю одну строку из списка, какой самый быстрый способ их проверить? Я думал о некотором хешировании, а затем о сравнении хэшей.
std::hash<std::string> hasher;
std::string stringToCheck = "Car";
switch (hasher(stringToCheck))
{
case hasher("Dog"):
break;
// and so on, I will ofcourse hash the whole list on program start and then compare against that.
}
Комментарии:
1. Вы, наверное, уже заметили, что
case hasher("Dog"):
не компилируется2. Возможно, вы заметили, что
case hasher("Dog"):
даже если бы это было скомпилировано, это привело бы к ложноположительным совпадениям.3. «Чем больше вы переосмысливаете водопровод, тем легче заткнуть дыру». — Скотти, Star Trek III.
==
Оператор уже достаточно быстр.4. Какой длины будет список? Для пяти элементов в вопросе достаточно просто поместить их в массив и выполнить линейный поиск с помощью
operator==
. Даже до нескольких сотен элементов, которых должно быть достаточно.
Ответ №1:
Я, конечно, буду хэшировать весь список при запуске программы, а затем сравнивать с этим.
Если это так, я бы предложил использовать стандартную библиотечную утилиту — std::unordered_set
, которая внутренне хранит данные и использует хэширование для их поиска:
int main() {
std::unordered_set<std::string> strings = {
"dog", "cat", "tree", "nicecar", "car"
};
std::string to_search = "dog";
std::cout << strings.contains(to_search); // true
std::cout << strings.contains("not there"); // false
}
Но обратите внимание, что это влечет за собой дополнительные затраты — создание набора (и, в действительности, любой способ предварительного хэширования данных) потребует дополнительного времени. Это стоит того, только если вы запрашиваете элементы из набора несколько раз. В противном случае будет достаточно простого старого ==
. Чтобы сравнить подходы, вы не можете сделать ничего другого, кроме как измерить время, затраченное на каждый из них.
РЕДАКТИРОВАТЬ: Похоже, вы хотите сохранить switch-case
функциональность. Для полноты картины, для достижения аналогичного эффекта я бы рекомендовал не set, а map . В частности, std::unordered_map
:
int main() {
std::unordered_map<std::string, void (*)()> cases = {
{"dog", [](){
std::cout << "this is a dog";
}},
{"cat", [](){
std::cout << "meow";
}},
{"tree", [](){
std::cout << "to isengard";
}},
{"nicecar", [](){
std::cout << "goes brrrr";
}},
{"car", [](){
std::cout << "wroom";
}}
};
const std::string to_search = "dog";
cases[to_search]();
}
Это выводит this is a dog
.
Единственным преимуществом этого подхода по сравнению с switch()
подходом является то, что с помощью этого контейнера вы можете динамически добавлять, удалять и изменять ключи и выполняемый код.
Комментарии:
1. это только говорит мне, есть ли строка в списке -> не работает в if else или переключается
2. Идентификатор выполняет.
if(strings.contains("not there")) { ... } else { ... }
. Если этого недостаточно, вам нужно отредактировать свой вопрос, чтобы показать, что бы вы хотели сделать с такими результатами.3. Или
strings.count("not there")
если не используется C 20.4. @jisojeb400 пожалуйста, посмотрите правку, так как я думаю, что теперь понимаю, что вы имели в виду. Если нет, не стесняйтесь поправлять меня.