Какой алгоритм поиска строк на самом деле самый быстрый?


27

Я застрял в течение некоторого времени на том, какой алгоритм поиска строк самый быстрый, услышал много мнений, но в итоге я не уверен.

Я слышал, как некоторые люди говорят, что самый быстрый алгоритм - это Бойер-Мур, а некоторые говорят, что Кнут-Моррис-Пратт на самом деле быстрее.

Я искал сложность на них обоих, но они в основном выглядят одинаково O(n+m). Я обнаружил, что в худшем случае Бойер-Мур имеет O(nm)сложность по сравнению с Кнутом-Моррисом-Праттом, который имеет O (m + 2 * n). Где n = длина текста и m = длина шаблона.

Насколько я знаю, у Бойера-Мура время линейного наихудшего случая, если бы я использовал правило Галиля.

Мой вопрос: над всем, что на самом деле является самым быстрым алгоритмом поиска в строке (этот вопрос включает в себя все возможные алгоритмы поиска, не только Бойера-Мура и Кнут-Морриса-Пратта)

Изменить: из-за этого ответа

То, что я точно ищу, это:

Учитывая текст Tи образец, Pя должен найти все появления Pв T.

Также длина P и T от, [1,2 000 000]и программа должна работать менее 0,15 сек.

Я знаю, что KMP и Рабин-Карп достаточно, чтобы получить 100% баллов по этой проблеме, но я, например, хотел попробовать реализовать Бойера-Мура. Что было бы лучше для этого типа поиска по шаблону?


6
Когда вы проверили их на своем языке выбора, что вы нашли?
Уолтер

4
В некоторых тестах Бойер-Мур был лучше, в других KMP был лучше, но я не уверен, что у меня есть «лучшая» реализация из них. Что касается языка выбора, он находится в тегах: C ++ (не уверен, что вы видели это, так как написали «язык выбора»). PS Я также не уверен, тестировал ли я лучшие тесты.
Вандамон Тайги


Кнут-Моррис-Пратт, у которого есть O (m + 2 * n) ... Вы имеете в виду O (m + n).
Жюль

Выберите один с приличной алгоритмической сложностью, а затем сделайте микро-настройку из этого с профилем в руке - всегда работал для меня. :-D

Ответы:


38

Это зависит от вида поиска, который вы хотите выполнить. Каждый из алгоритмов работает особенно хорошо для определенных типов поиска, но вы не указали контекст своих поисков.

Вот некоторые типичные мысли о типах поиска:

  • Бойер-Мур: работает, предварительно анализируя паттерн и сравнивая справа налево. Если происходит несоответствие, первоначальный анализ используется для определения того, насколько далеко шаблон может быть смещен относительно искомого текста. Это особенно хорошо работает для длинных шаблонов поиска. В частности, он может быть сублинейным, так как вам не нужно читать каждый символ вашего текста.

  • Кнут-Моррис-Пратт: также предварительно анализирует паттерн, но пытается повторно использовать то, что уже было найдено в начальной части паттерна, чтобы избежать необходимости повторного сопоставления. Это может работать довольно хорошо, если ваш алфавит мал (например, базы ДНК), поскольку у вас больше шансов, что ваши шаблоны поиска содержат многократно используемые подшаблоны.

  • Ахо-Корасик: Требуется много предварительной обработки, но для ряда паттернов. Если вы знаете, что будете искать одни и те же шаблоны поиска снова и снова, тогда это намного лучше, чем другие, потому что вам нужно анализировать шаблоны только один раз, а не один раз за поиск.

Следовательно, как обычно в CS, нет однозначного ответа на все лучшее . Это скорее вопрос выбора правильного инструмента для работы под рукой.

Еще одно замечание о вашем наихудшем рассуждении: рассмотрите виды поиска, необходимые для создания этого худшего случая, и тщательно продумайте, действительно ли они актуальны в вашем случае. Например, O(mn)сложность алгоритма Бойера-Мура в худшем случае проистекает из шаблона поиска и текста, каждый из которых использует только один символ (например, при поиске aaaв aaaaaaaaaaaaaaaaaaaaa) - вам действительно нужно быть быстрым для таких поисков?


У меня есть целый английский алфавит или около того, и я обновил Вопрос, извините, что не начал с этого в начале.
vandamon taigi

И да, мне нужно быть быстрым даже для таких поисков
vandamon taigi

1

Хотя я немного опаздываю, чтобы ответить на этот вопрос, но я думаю, что Z-Algorithmэто намного быстрее, чем любые его аналоги. Его сложность в худшем случае составляет O (m + n), и он не требует предварительной обработки шаблона / текста. Это также очень легко кодировать по сравнению с другими алгоритмами.

Это работает следующим образом.

Например, есть строка S ='abaaba'. Мы должны найти z(i)значения для i=0 to len(S)-1. Прежде чем углубляться в объяснение, позвольте мне сначала изложить некоторые определения.

z(i)= нет символов префикса S, совпадающего с префиксом s(i).

s(i)= ithСуффикс S.

Ниже приведены s(i)значения для s = 'abaaba'.

s(0) = 'abaaba' = S
s(1) = 'baaba'
s(2) = 'aaba'
s(3) = 'aba'
s(4) = 'ba'
s(5) = 'a'

Значения z соответственно

z(0) = 6 = length(S)
z(1) = 0
z(2) = 1
z(3) = 3
z(4) = 0
z(5) = 1

Для детального понимания алгоритма, обратитесь к следующим ссылкам.

http://codeforces.com/blog/entry/3107

https://www.youtube.com/watch?v=MFK0WYeVEag

Теперь требуется O (N), чтобы найти все zзначения без каких-либо затрат на предварительную обработку. Теперь было бы интересно узнать, как вы можете использовать эту логику для сопоставления с шаблоном в данной строке?

Давайте посмотрим на примере. Шаблон (Р): aba, Текст (Т): aacbabcabaad.

Поместите это в форму P $ T. ( $- любой символ, который не появляется ни в шаблоне, ни в тексте. Я скоро пойду к важности $.)

P$T знак равно aba$aacbabcabaad

Мы знаем len(P)= 3.

Все значения z P$Tявляются

z(0) = 16 = len(P$T)
z(1) = 0
z(2) = 1
z(3) = 0
z(4) = 1
z(5) = 1
z(6) = 0
z(7) = 0
z(8) = 2
z(9) = 0
z(10) = 0
z(11) = 3
z(12) = 0
z(13) = 1
Z(14) = 1
Z(15) = 0

Теперь, который z(i)= len(P). Ans = 11.Таким образом, наша модель присутствует в Ans-len(P)-1= 7. -1для $персонажа.

Теперь, почему $или любой такой особый характер важен. Рассмотрим P = 'aaa'и T = 'aaaaaaa'. Без специального символа все z(i)будут иметь инкрементные значения. Еще можно найти положение шаблона в тексте с помощью следующих формул:

Условие: z(i)> = len(P)и положение: Ans-len(P). Но состояние в этом случае становится немного сложным и запутанным. Я лично предпочитаю использовать специальную технику персонажа.


1
Не могли бы вы объяснить это здесь? Наличие ссылок на внешние сайты может быть использовано для уточнения, но суть ответа должна заключаться в самом ответе, а не в том, чтобы переходить по ссылке на другой сайт.

Z-алгоритм в основном такой же, как kmp. Я сомневаюсь, что это намного быстрее.
Томас Але

2
Я согласен с @ThomasAhle. Вычислительная обработка z - это предварительная обработка. Хотя это хорошее объяснение. O(n)Благодаря этому ответу я изобрел способ преобразования предварительной обработки KMP в предварительную обработку Z. Здесь
leewz

-1

Использование адресной памяти контента , реализованной в программном обеспечении в виде виртуальной адресации (указание букв к буквам).

Это своего рода излишний алгоритм сопоставления средней строки.

CAM может одновременно соответствовать огромному количеству шаблонов, вплоть до 128-буквенных шаблонов (если они являются ASCII; если они только в Unicode 64). И это один вызов на длину буквы в строке, которой вы хотите соответствовать, и одно случайное чтение из памяти на длину максимальной длины шаблона. Таким образом, если вы анализируете строку длиной 100 000 букв с одновременным использованием до 90 000 000 шаблонов (для хранения такого большого количества шаблонов потребуется около 128 ГиБ), это займет 12 800 000 случайных операций чтения из ОЗУ, поэтому это произойдет за 1 мс.

Вот как работает виртуальная адресация.

Если я начну с 256 начальных адресов, которые представляют первую букву, эти буквы указывают на 256 следующих букв. Если шаблон не существует, вы не сохраните его.

Поэтому, если я продолжаю связывать буквы с буквами, это похоже на наличие 128 фрагментов виртуальной адресации, указывающих на виртуальную адресацию.

Это сработает - но чтобы одновременно получить 900 000 000 шаблонов, есть еще один трюк, который нужно добавить к этому - и он использует тот факт, что вы начинаете с большого повторного использования этих буферов писем, но позже это рассеивается. Если вы перечислите содержимое вместо того, чтобы выделять все 256 символов, то оно очень мало замедлится, и вы получите увеличение емкости в 100 раз, потому что в основном вы в конечном итоге получаете только 1 букву, используемую в каждом буфере указателя букв (который я назвал ' побег').

Если вы хотите получить совпадение строки ближайшего соседа, то у вас есть много таких, которые работают параллельно, и вы собираете их в иерархии, поэтому вы распределяете свою ошибку беспристрастно. если вы пытаетесь найти ближайшего соседа только с одним, то вы смещены к началу дерева.


4
@MagnusRobertCarlWoot, учитывая, что у вас тот же gavatar, что и roucer81, это либо астрономическое совпадение коллизии хеш-кода, либо у вас тот же адрес электронной почты. Если вы являетесь одним и тем же лицом за обеими учетными записями, вы должны использовать форму «связаться с нами», чтобы объединить их, чтобы получить надлежащую оценку репутации, полученной благодаря ответам на этот ответ.
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.