Сравнение строки со списком строк в C

#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 пожалуйста, посмотрите правку, так как я думаю, что теперь понимаю, что вы имели в виду. Если нет, не стесняйтесь поправлять меня.