Итак, я не похож на идиота, я собираюсь изложить проблему / требования более четко:
- Игла (образец) и стог сена (текст для поиска) - строки с нулевым окончанием в стиле C. Информация о длине не предоставляется; если необходимо, оно должно быть вычислено.
- Функция должна вернуть указатель на первое совпадение или,
NULL
если совпадение не найдено. - Случаи отказа не допускаются. Это означает, что любой алгоритм с непостоянными (или большими постоянными) требованиями к хранилищу должен иметь запасной вариант для сбоя выделения (и, следовательно, производительность в резервном режиме повышает производительность в худшем случае).
- Реализация должна быть на C, хотя хорошее описание алгоритма (или ссылка на него) без кода тоже подойдет.
... а также то, что я имею в виду под "самым быстрым":
- Детерминированный
O(n)
гдеn
= длина стога сена. (Но может быть возможно использовать идеи из алгоритмов, которые обычноO(nm)
(например, скользящий хэш), если они объединены с более надежным алгоритмом для получения детерминированныхO(n)
результатов). - Никогда не работает (измеримо; пара часов
if (!needle[1])
и т. Д. В порядке) хуже, чем алгоритм наивной грубой силы, особенно на очень коротких иглах, которые, вероятно, являются наиболее распространенным случаем. (Безусловные тяжелые накладные расходы на предварительную обработку плохие, так как пытаются улучшить линейный коэффициент для патологических игл за счет вероятных игл.) - Учитывая произвольную иголку и стог сена, сопоставимая или лучшая производительность (не хуже, чем на 50% больше времени поиска) по сравнению с любым другим широко реализованным алгоритмом.
- Помимо этих условий, я оставляю определение «самый быстрый» открытый. Хороший ответ должен объяснить, почему вы считаете подход, который вы предлагаете, «самым быстрым».
Моя текущая реализация работает примерно на 10% медленнее и в 8 раз быстрее (в зависимости от ввода), чем реализация glibc Two-Way.
Обновление: мой текущий оптимальный алгоритм выглядит следующим образом:
- Для игл длиной 1 используйте
strchr
. - Для игл длиной 2-4 используйте машинные слова, чтобы сравнить 2-4 байта одновременно следующим образом: предварительно загрузить иглу в 16- или 32-битное целое число со смещениями и циклически выводить старые байты / новые байты из стога сена на каждой итерации , Каждый байт стога сена читается ровно один раз и выполняет проверку на 0 (конец строки) и одно 16- или 32-битное сравнение.
- Для игл длиной> 4 используйте двусторонний алгоритм с плохой таблицей сдвига (например, Бойера-Мура), который применяется только к последнему байту окна. Чтобы избежать затрат на инициализацию таблицы размером 1 Кб, что может привести к чистым потерям для многих игл средней длины, я сохраняю битовый массив (32 байта), отмечающий, какие записи в таблице сдвига инициализированы. Биты, которые не установлены, соответствуют значениям байтов, которые никогда не появляются в игле, для которых возможен полный сдвиг длины иглы.
Большие вопросы, которые остались в моей голове:
- Есть ли способ лучше использовать таблицу плохих смен? Бойер-Мур лучше всего использует его, сканируя в обратном направлении (справа налево), но для двухстороннего сканирования требуется сканирование слева направо.
- Единственные два жизнеспособные алгоритмы кандидатов , которые я нашел в общем случае (не из- за нехватками памяти или квадратичных условий исполнения) являются двухсторонние и Струнный Matching на упорядоченных алфавитах . Но есть ли легко обнаруживаемые случаи, когда разные алгоритмы были бы оптимальными? Конечно, многие из
O(m)
(гдеm
длина иглы) в космических алгоритмах могут быть использованы дляm<100
или около того. Также было бы возможно использовать алгоритмы, являющиеся квадратичными в худшем случае, если бы существовал простой тест для игл, который доказуемо требует только линейного времени.
Бонусные баллы за:
- Можете ли вы улучшить производительность, если предположить, что игла и стог сена хорошо сформированы UTF-8? (С символами различной длины байтов правильная форма накладывает некоторые требования к выравниванию строк между иглой и стогом сена и позволяет автоматически сдвигать 2-4 байта, когда встречается несоответствующий главный байт. Но действительно ли эти ограничения дают вам многое / что-либо помимо того, что вычисления максимального суффикса, хорошие сдвиги суффиксов и т. д. уже дают вам различные алгоритмы?)
Примечание: я хорошо знаю большинство алгоритмов, но не знаю, насколько хорошо они работают на практике. Вот хороший справочник, чтобы люди не давали мне ссылки на алгоритмы в виде комментариев / ответов: http://www-igm.univ-mlv.fr/~lecroq/string/index.html
strstr
как что-то на потом, так что на самом деле я не удосужился правильно прочитать статью, на которую вы ссылались, но это звучит очень многообещающе. Спасибо и извините, что не вернулись к вам.