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