Поиск подстроки: различия между версиями

Материал из Википедии — свободной энциклопедии
Перейти к навигации Перейти к поиску
[непроверенная версия][отпатрулированная версия]
Содержимое удалено Содержимое добавлено
Преамбула: перенесена на КУ
 
(не показано 48 промежуточных версий 22 участников)
Строка 1: Строка 1:
<noinclude>{{к удалению|2021-12-05}}</noinclude>
''[[Поиск информации]]'' — одно из основных использований [[компьютер]]а. Одна из простейших задач поиска информации — '''поиск точно заданной подстроки в строке'''. Тем не менее, эта задача чрезвычайно важна — она применяется в [[текстовый редактор|текстовых редакторах]], [[СУБД]], [[поисковая машина|поисковых машинах]]…
'''Поиск подстроки в строке''' — одна из простейших задач [[Поиск информации|поиска информации]]. Применяется в виде встроенной функции в [[текстовый редактор|текстовых редакторах]], [[СУБД]], [[поисковая машина|поисковых машинах]], [[язык программирования|языках программирования]] и т. п.


В задачах поиска традиционно принято обозначать шаблон поиска как ''needle'' ({{lang-en|«иголка»}}), а строку, в которой ведётся поиск — как ''haystack'' ({{lang-en|«[[wikt:искать иголку в стоге сена|стог сена]]»}}). Также обозначим через Σ алфавит, на котором проводится поиск.
В задачах поиска традиционно принято обозначать шаблон поиска как ''needle'' ({{tr-en|иголка}}), а строку, в которой ведётся поиск — как ''haystack'' ({{tr-en|[[wikt:искать иголку в стоге сена|стог сена]]}}). Также обозначим через Σ алфавит, на котором проводится поиск.{{Нет АИ|4|1|2016}}


== Несостоятельность примитивного алгоритма ==
== Несостоятельность примитивного алгоритма ==
Если считать, что строки нумеруются с 1, простейший алгоритм ({{lang-en|brute force algorithm, naïve algorithm}}) выглядит так.

Если считать, что строки нумеруются с 1, простейший алгоритм ({{lang-en|brute force algorithm, naïve algorigthm}}) выглядит так.


for i=0...|haystack|-|needle|
for i=0...|haystack|-|needle|
for j=1...|needle|
for j=0...|needle|
if haystack[i+j]<>needle[j]
if haystack[i+j + 1]<>needle[j]
then goto 1
then goto 1
output("Найдено: ", i+1)
output("Найдено: ", i+1)
1:
1:


Простейший алгоритм поиска даже в ''лучшем'' случае проводит |''haystack''|&minus;|''needle''|+1 сравнение; если же есть много частичных совпадений, скорость снижается до ''O''(|''haystack''|·|''needle''|).
Простейший алгоритм поиска даже в ''лучшем'' случае проводит |''haystack''||''needle''|+1 сравнение; если же есть много частичных совпадений, скорость снижается до ''O''(|''haystack''|·|''needle''|).


Показано, что примитивный алгоритм отрабатывает в среднем 2''h'' сравнений<ref>[http://www-igm.univ-mlv.fr/~lecroq/string/node3.html Brute force algorithm]{{ref-en}}</ref>.
Доказано, что примитивный алгоритм отрабатывает в среднем 2''h'' сравнений<ref>[http://www-igm.univ-mlv.fr/~lecroq/string/node3.html Brute force algorithm] {{Wayback|url=http://www-igm.univ-mlv.fr/~lecroq/string/node3.html |date=20090121190217 }}{{ref-en}}</ref>.


== Для чего нужно так много алгоритмов? ==
== Для чего нужно так много алгоритмов? ==

На сегодняшний день существует огромное разнообразие алгоритмов поиска подстроки. Программисту приходится выбирать подходящий в зависимости от таких факторов.
На сегодняшний день существует огромное разнообразие алгоритмов поиска подстроки. Программисту приходится выбирать подходящий в зависимости от таких факторов.
# Нужна ли вообще оптимизация, или хватает примитивного алгоритма? Как правило, именно его реализуют стандартные библиотеки языков программирования.
# «Враждебность» пользователя. Другими словами: будет ли пользователь намеренно задавать данные, на которых алгоритм будет медленно работать? Существуют очень простые алгоритмы, оценка которых ''O''(|''haystack''|·|''needle''|) в худшем случае, но на «обычных» данных количество сравнений намного меньше |''haystack''|. Только в [[1990-е]] годы были созданы алгоритмы, дающие сложность ''O''(|''haystack''|) в худшем случае и меньше |''haystack''| в среднем.
# «Враждебность» пользователя. Другими словами: будет ли пользователь намеренно задавать данные, на которых алгоритм будет медленно работать? Существуют очень простые алгоритмы, оценка которых ''O''(|''haystack''|·|''needle''|) в худшем случае, но на «обычных» данных количество сравнений намного меньше |''haystack''|. Только в [[1990-е]] годы были созданы алгоритмы, дающие сложность ''O''(|''haystack''|) в худшем случае и меньше |''haystack''| в среднем.
# [[Грамматика (наука)|Грамматика]] языка может быть недружественной к тем или иным эвристикам, которые ускоряют поиск «в среднем».
# [[Грамматика (наука)|Грамматика]] языка может быть недружественной к тем или иным эвристикам, которые ускоряют поиск «в среднем».
# Архитектура процессора. Некоторые процессоры имеют [[автоинкрементная операция|автоинкрементные]] или [[SIMD]]-операции, которые позволяют быстро сравнить два участка ОЗУ (например, <code>rep cmpsd</code> на [[x86]]). На таких процессорах заманчиво применить алгоритм, который просто бы сравнивал ''needle'' с ''haystack'' — разумеется, не во всех позициях.
# [[Архитектура процессора]]. Некоторые процессоры имеют [[автоинкрементная операция|автоинкрементные]] или [[SIMD]]-операции, которые позволяют быстро сравнить два участка ОЗУ (например, <code>rep cmpsd</code> на [[x86]]). На таких процессорах заманчиво применить алгоритм, который просто бы сравнивал ''needle'' с ''haystack'' — разумеется, не во всех позициях.
# Размер алфавита. Многие алгоритмы (особенно основанные на сравнении с конца) имеют эвристики, связанные с несовпавшим символом. На больших алфавитах таблица символов будет занимать много памяти, на малых — соответствующая эвристика будет неэффективной.
# Размер алфавита. Многие алгоритмы (особенно основанные на сравнении с конца) имеют эвристики, связанные с несовпавшим символом. На больших алфавитах таблица символов будет занимать много памяти, на малых — соответствующая эвристика будет неэффективной.
# Возможность проиндексировать ''haystack''. Если таковая есть, поиск серьёзно ускорится.
# Возможность проиндексировать ''haystack''. Если таковая есть, поиск серьёзно ускорится.
# Требуется ли одновременный поиск нескольких строк? Приблизительный поиск? Побочные свойства некоторых алгоритмов ([[алгоритм Ахо-Корасик|Ахо-Корасик]], [[Двоичный алгоритм поиска подстроки|двоичного алгоритма]]) позволяют такое.
# Требуется ли одновременный поиск нескольких строк? Приблизительный поиск? Побочные свойства некоторых алгоритмов ([[алгоритм Ахо-Корасик|Ахо-Корасик]], [[Двоичный алгоритм поиска подстроки|двоичного алгоритма]]) позволяют такое.


Как правило, в [[текстовый редактор|текстовом редакторе]] достаточно взять самый простой эвристический алгоритм наподобие [[алгоритм Бойера-Мура-Хорспула|Бойера-Мура-Хорспула]] — даже очень медленный ПК справится с поиском за доли секунды. Если же объём текста измеряется гигабайтами, либо поиск запущен на сервере, который обрабатывает множество запросов — приходится выбирать наиболее удачный алгоритм из доступных. Например, программы [[Определение плагиата|определения плагиата]] осуществляют онлайн проверку, используя алгоритмы поиска подстроки среди большого количества документов, хранящихся в собственной базе.
Как правило, в [[текстовый редактор|текстовом редакторе]] достаточно взять самый простой эвристический алгоритм наподобие [[алгоритм БойераМураХорспула|Бойера — Мура — Хорспула]] — даже очень медленный ПК справится с поиском за доли секунды. Если же объём текста измеряется гигабайтами, либо поиск запущен на сервере, который обрабатывает множество запросов — приходится выбирать наиболее удачный алгоритм из доступных. Например, программы [[Определение плагиата|определения плагиата]] осуществляют онлайн-проверку, используя алгоритмы поиска подстроки среди большого количества документов, хранящихся в собственной базе.


== Алгоритмы ==
== Алгоритмы ==

Для сокращения обозначим:
Для сокращения обозначим:
* |Σ|=σ — размер алфавита.
* |Σ|=σ — размер алфавита.
Строка 39: Строка 38:
Вычислительная сложность определяется ''до первого совпадения''. '''Жирным шрифтом''' выделены важнейшие с практической точки зрения алгоритмы.
Вычислительная сложность определяется ''до первого совпадения''. '''Жирным шрифтом''' выделены важнейшие с практической точки зрения алгоритмы.


=== Основанные на сравнении как «чёрном ящике» ===
=== Основанные на сравнении как «[[Чёрный ящик|чёрном ящике]]» ===
Во всех этих алгоритмах точка, где «иголка» не совпала со «стогом сена», не участвует в принятии решения. Это позволяет использовать стандартные функции [[memcmp|сравнения участков памяти]], зачастую оптимизированные на ассемблерном уровне под тот или иной процессор.

Во всех этих алгоритмах сравнение строк является «[[чёрный ящик|чёрным ящиком]]». Это позволяет использовать стандартные функции сравнения участков памяти, зачастую оптимизированные на ассемблерном уровне под тот или иной процессор и не выдающие точки, в которой наступило несовпадение.


К этой категории относится и примитивный алгоритм поиска.
К этой категории относится и примитивный алгоритм поиска.
Строка 60: Строка 58:
|
|
|-
|-
| '''[[Алгоритм Бойера-Мура-Хорспула]]'''
| '''[[Алгоритм Бойера — Мура — Хорспула]]'''
| ''O''(''n''+σ)
| ''O''(''n''+σ)
| ~ 2''H'' / σ<ref>{{Cite web |url=http://www-igm.univ-mlv.fr/~lecroq/string/node18.html#SECTION00180 |title=Horspool algorithm<!-- Заголовок добавлен ботом --> |access-date=2013-05-02 |archive-date=2012-08-29 |archive-url=https://web.archive.org/web/20120829203936/http://www-igm.univ-mlv.fr/~lecroq/string/node18.html#SECTION00180 |deadlink=no }}</ref>
| <''H''
| ''O''(''Hn'')
| ''O''(''Hn'')
| Упрощённый до предела алгоритм Бойера-Мура; использует только видоизменённую эвристику стоп-символа — за стоп-символ всегда берётся символ ''haystack'', расположенный напротив последнего символа ''needle''.
| Упрощённый до предела алгоритм Бойера — Мура; использует только видоизменённую эвристику стоп-символа — за стоп-символ всегда берётся символ ''haystack'', расположенный напротив последнего символа ''needle''.
|-
|-
| '''[[Алгоритм быстрого поиска подстроки|Алгоритм быстрого поиска]]'''<br />=[[Алгоритм Санди]]
| '''[[Алгоритм быстрого поиска подстроки|Алгоритм быстрого поиска]]'''<br>[[Алгоритм Санди]]
| ''O''(''n''+σ)
| ''O''(''n''+σ)
| <''H''
| <''H''
Строка 74: Строка 72:


=== Основанные на сравнении с начала ===
=== Основанные на сравнении с начала ===

Это семейство алгоритмов страдает невысокой скоростью на «хороших» данных, что компенсируется отсутствием регрессии на «плохих».
Это семейство алгоритмов страдает невысокой скоростью на «хороших» данных, что компенсируется отсутствием регрессии на «плохих».


Строка 90: Строка 87:
| <''H''+''n''
| <''H''+''n''
| ''O''(''Hn'')
| ''O''(''Hn'')
| [[Хэширование]] позволяет серьёзно снизить сложность в среднем
| [[Хеширование]] позволяет серьёзно снизить сложность в среднем
|-
|-
| '''Автоматный алгоритм'''<br />=[[Алгоритм Ахо-Корасик]]
| {{nobr|'''Автоматный алгоритм'''}}<br>[[Алгоритм Ахо-Корасик]]
| ''O''(''n''σ)
| ''O''(''n''σ)
| colspan=2 align=center | = ''H''
|
| {{nobr|≤ ''H'' + min(''H'', ''n'')}}
| Строит [[конечный автомат]], который распознаёт язык, состоящий из одной-единственной строки. После небольшой модификации позволяет за один проход по ''haystack'' найти одну строку из нескольких.
| Строит [[конечный автомат]], который распознаёт язык, состоящий из одной-единственной строки. После небольшой модификации позволяет за один проход по ''haystack'' найти одну строку из нескольких.
|-
|-
| '''[[Алгоритм Кнута-Морриса-Пратта]]'''
| '''[[Алгоритм Кнута-Морриса-Пратта]]'''
| ''O''(''n'')
| ''O''(''n'')
| colspan=2 align=center | ≤ 2''H''
|
| Один из первых алгоритмов с линейной оценкой в худшем случае. Модификация алгоритма Ахо-Корасик, строящая автомат неявно на основе префикс-функции.
| {{nobr|≤ ''H'' + min(''H'', ''n'')}}
| Один из первых алгоритмов с линейной оценкой в худшем случае. По сути, это алгоритм Ахо-Корасик с неявным построением автомата.
|-
|-
| [[Алгоритм Апостолико-Крошмора]]
| [[Алгоритм Апостолико-Крошмора]]
| ''O''(''n'')
| ''O''(''n'')
| < ''H''
|
| &le;1,5''H''
| ≤1,5''H''
|
|
|-
|-
| '''[[Алгоритм Shift-Or]]'''<br />=[[Bitap-алгоритм]]<br />=[[Двоичный алгоритм поиска подстроки|Двоичный алгоритм]]
| '''[[Алгоритм Shift-Or]]'''<br>[[Bitap-алгоритм]]<br>[[Двоичный алгоритм поиска подстроки|Двоичный алгоритм]]
| ''O''(''n''+σ)
| ''O''(''n''+σ)
| colspan=2 align=center | {{nobr|1==''H''·[[потолок числа|ceil]](''n/w'')}}
| colspan=2 align=center | {{nobr|1==''H''·[[потолок числа|ceil]](''n/w'')}}
Строка 117: Строка 112:


=== Основанные на сравнении с конца ===
=== Основанные на сравнении с конца ===

В этом семействе алгоритмов ''needle'' движется по ''haystack'' слева направо, но сравнение этих строк друг с другом проводится справа налево. Сравнение справа налево позволяет в случае несовпадения сдвинуть ''needle'' не на одну позицию, а на несколько.
В этом семействе алгоритмов ''needle'' движется по ''haystack'' слева направо, но сравнение этих строк друг с другом проводится справа налево. Сравнение справа налево позволяет в случае несовпадения сдвинуть ''needle'' не на одну позицию, а на несколько.


Строка 129: Строка 123:
! макс.
! макс.
|-
|-
| '''[[Алгоритм Бойера-Мура]]'''
| '''[[Алгоритм БойераМура]]'''
| ''O''(''n''+σ)
| ''O''(''n''+σ)
| <''H''
| <''H''
| ''O''(''Hn'')
| ''O''(''Hn'')
| Стандартный алгоритм поиска подстроки в строке. Считается наиболее эффективным алгоритмом общего назначения.<ref>{{Cite web |url=http://www-igm.univ-mlv.fr/~lecroq/string/node14.html#SECTION00140 |title=Boyer-Moore algorithm<!-- Заголовок добавлен ботом --> |access-date=2013-05-02 |archive-date=2013-02-06 |archive-url=https://web.archive.org/web/20130206123458/http://www-igm.univ-mlv.fr/~lecroq/string/node14.html#SECTION00140 |deadlink=no }}</ref>
| Стандартный алгоритм поиска подстроки в строке
|-
|-
| [[Алгоритм Чжу-Такаоки]]
| [[Алгоритм Чжу-Такаоки]]
Строка 139: Строка 133:
| <''H''
| <''H''
| ''O''(''Hn'')
| ''O''(''Hn'')
| Алгоритм Бойера-Мура, оптимизированный под короткие алфавиты
| Алгоритм Бойера — Мура, оптимизированный под короткие алфавиты
|-
|-
| [[Алгоритм Апостолико-Джанкарло]]
| [[Алгоритм Апостолико-Джанкарло]]
| ''O''(''n''+σ)
| ''O''(''n''+σ)
| <''H''
| <''H''
| &le;1,5''H''
| ≤1,5''H''
| Одна из первых попыток получить <''H'' в типичном случае и ''O''(''H'') в худшем. Очень сложен в реализации.
| Одна из первых попыток получить <''H'' в типичном случае и ''O''(''H'') в худшем. Очень сложен в реализации.
|-
|-
| [[Турбо-алгоритм Бойера-Мура]]
| [[Турбо-алгоритм БойераМура]]
| ''O''(''n''+σ)
| ''O''(''n''+σ)
| <''H''
| <''H''
| &le;2''H''
| ≤2''H''
| Один из наиболее эффективных алгоритмов, не дающих регрессии на «плохих» данных
| Один из наиболее эффективных алгоритмов, не дающих регрессии на «плохих» данных
|}
|}


=== Проводящие сравнение в необычном порядке ===
=== Проводящие сравнение в необычном порядке ===

{| class="wikitable"
{| class="wikitable"
! rowspan=2 | Название
! rowspan=2 | Название
Строка 171: Строка 164:
| Простой алгоритм, сравнивающий второй символ, затем начиная с третьего в режиме «чёрного ящика», и, наконец, первый. При {{nobr|''n''[1]≠''n''[2]}}<ref>Напомним, символы нумеруются с 1, как в [[Паскаль (язык программирования)|Паскале]].</ref> и несовпадении на второй-третьей стадии — сдвиг на 2 вправо.
| Простой алгоритм, сравнивающий второй символ, затем начиная с третьего в режиме «чёрного ящика», и, наконец, первый. При {{nobr|''n''[1]≠''n''[2]}}<ref>Напомним, символы нумеруются с 1, как в [[Паскаль (язык программирования)|Паскале]].</ref> и несовпадении на второй-третьей стадии — сдвиг на 2 вправо.
|-
|-
| Алгоритм Райты<br />=Алгоритм Бойера-Мура-Хорспула-Райты
| Алгоритм Райты<br>Алгоритм Бойера — Мура — Хорспула — Райты
| ''O''(''n''+σ)
| ''O''(''n''+σ)
| <''H''
| <''H''
Строка 178: Строка 171:
|}
|}


== См. также ==
{{compu-stub}}
* [[Подстрока]]
* [[Список алгоритмов#Алгоритмы на строках|Алгоритмы на строках]]
* [[Сопоставление с образцом]]
* [[Алгоритмы: построение и анализ]]


== Примечания ==
== Примечания ==
{{примечания}}
{{примечания}}


== Источники ==
== Литература ==
* {{книга
|автор = {{nobr|Гасфилд Д.}}
|заглавие = Строки, деревья и последовательности в алгоритмах: Информатика и вычислительная биология
|оригинал = Algorithms on String, Trees, and Sequences. Computer Science and Computational Biology
|ссылка =
|ответственный = Пер. с англ. И. В. Романовского
|издание = 2-е изд
|место = СПб.
|издательство = Невский Диалект
|год = 2003
|страниц = 654
|isbn = 5-7940-0103-8, 5-94157-321-9, 0-521-58519-8
}}
* {{книга
|автор = {{nobr|Смит Б.}}
|заглавие = Методы и алгоритмы вычислений на строках
|оригинал = Computing Patterns in Strings
|ссылка =
|ответственный =
|издание =
|место = М.
|издательство = Вильямс
|год = 2006
|страниц = 496
|isbn = 5-8459-1081-1, 0-201-39839-7
}}
* {{книга
|автор = {{nobr|Окулов С. М.}}
|заглавие = Алгоритмы обработки строк
|ссылка =
|ответственный =
|издание =
|место = М.
|издательство = Бином
|год = 2013
|страниц = 255
|isbn = 978-5-9963016-2-1
}}


== Ссылки ==
* [http://www-igm.univ-mlv.fr/~lecroq/string/index.html Большая подборка алгоритмов поиска подстроки]{{ref-en}}
* [http://www-igm.univ-mlv.fr/~lecroq/string/index.html Большая подборка алгоритмов поиска подстроки]{{ref-en}}
{{Строки}}{{rq|style|wikify|check}}


[[Категория:Поиск подстроки|*]]
[[Категория:Поиск подстроки| ]]

Текущая версия от 11:43, 25 августа 2023

Поиск подстроки в строке — одна из простейших задач поиска информации. Применяется в виде встроенной функции в текстовых редакторах, СУБД, поисковых машинах, языках программирования и т. п.

В задачах поиска традиционно принято обозначать шаблон поиска как needle (с англ. — «иголка»), а строку, в которой ведётся поиск — как haystack (с англ. — «стог сена»). Также обозначим через Σ алфавит, на котором проводится поиск.[источник не указан 3291 день]

Несостоятельность примитивного алгоритма

[править | править код]

Если считать, что строки нумеруются с 1, простейший алгоритм (англ. brute force algorithm, naïve algorithm) выглядит так.

for i=0...|haystack|-|needle|
  for j=0...|needle|
    if haystack[i+j + 1]<>needle[j] 
      then goto 1
  output("Найдено: ", i+1)
  1:

Простейший алгоритм поиска даже в лучшем случае проводит |haystack|−|needle|+1 сравнение; если же есть много частичных совпадений, скорость снижается до O(|haystack|·|needle|).

Доказано, что примитивный алгоритм отрабатывает в среднем 2h сравнений[1].

Для чего нужно так много алгоритмов?

[править | править код]

На сегодняшний день существует огромное разнообразие алгоритмов поиска подстроки. Программисту приходится выбирать подходящий в зависимости от таких факторов.

  1. Нужна ли вообще оптимизация, или хватает примитивного алгоритма? Как правило, именно его реализуют стандартные библиотеки языков программирования.
  2. «Враждебность» пользователя. Другими словами: будет ли пользователь намеренно задавать данные, на которых алгоритм будет медленно работать? Существуют очень простые алгоритмы, оценка которых O(|haystack|·|needle|) в худшем случае, но на «обычных» данных количество сравнений намного меньше |haystack|. Только в 1990-е годы были созданы алгоритмы, дающие сложность O(|haystack|) в худшем случае и меньше |haystack| в среднем.
  3. Грамматика языка может быть недружественной к тем или иным эвристикам, которые ускоряют поиск «в среднем».
  4. Архитектура процессора. Некоторые процессоры имеют автоинкрементные или SIMD-операции, которые позволяют быстро сравнить два участка ОЗУ (например, rep cmpsd на x86). На таких процессорах заманчиво применить алгоритм, который просто бы сравнивал needle с haystack — разумеется, не во всех позициях.
  5. Размер алфавита. Многие алгоритмы (особенно основанные на сравнении с конца) имеют эвристики, связанные с несовпавшим символом. На больших алфавитах таблица символов будет занимать много памяти, на малых — соответствующая эвристика будет неэффективной.
  6. Возможность проиндексировать haystack. Если таковая есть, поиск серьёзно ускорится.
  7. Требуется ли одновременный поиск нескольких строк? Приблизительный поиск? Побочные свойства некоторых алгоритмов (Ахо-Корасик, двоичного алгоритма) позволяют такое.

Как правило, в текстовом редакторе достаточно взять самый простой эвристический алгоритм наподобие Бойера — Мура — Хорспула — даже очень медленный ПК справится с поиском за доли секунды. Если же объём текста измеряется гигабайтами, либо поиск запущен на сервере, который обрабатывает множество запросов — приходится выбирать наиболее удачный алгоритм из доступных. Например, программы определения плагиата осуществляют онлайн-проверку, используя алгоритмы поиска подстроки среди большого количества документов, хранящихся в собственной базе.

Для сокращения обозначим:

  • |Σ|=σ — размер алфавита.
  • |haystack|=H — длина строки, в которой ведётся поиск.
  • |needle|=n — длина шаблона поиска.

Вычислительная сложность определяется до первого совпадения. Жирным шрифтом выделены важнейшие с практической точки зрения алгоритмы.

Основанные на сравнении как «чёрном ящике»

[править | править код]

Во всех этих алгоритмах точка, где «иголка» не совпала со «стогом сена», не участвует в принятии решения. Это позволяет использовать стандартные функции сравнения участков памяти, зачастую оптимизированные на ассемблерном уровне под тот или иной процессор.

К этой категории относится и примитивный алгоритм поиска.

Название Предв. обработка Сложность Примечания
типичная макс.
Примитивный алгоритм Нет 2H O(Hn)
Алгоритм Бойера — Мура — Хорспула O(n+σ) ~ 2H / σ[2] O(Hn) Упрощённый до предела алгоритм Бойера — Мура; использует только видоизменённую эвристику стоп-символа — за стоп-символ всегда берётся символ haystack, расположенный напротив последнего символа needle.
Алгоритм быстрого поиска
Алгоритм Санди
O(n+σ) <H O(Hn) Также использует исключительно эвристику стоп-символа — но за стоп-символ берётся символ haystack, идущий за последним символом needle.

Основанные на сравнении с начала

[править | править код]

Это семейство алгоритмов страдает невысокой скоростью на «хороших» данных, что компенсируется отсутствием регрессии на «плохих».

Название Предв. обработка Сложность Примечания
типичная макс.
Алгоритм Рабина-Карпа O(n) <H+n O(Hn) Хеширование позволяет серьёзно снизить сложность в среднем
Автоматный алгоритм
Алгоритм Ахо-Корасик
O(nσ) = H Строит конечный автомат, который распознаёт язык, состоящий из одной-единственной строки. После небольшой модификации позволяет за один проход по haystack найти одну строку из нескольких.
Алгоритм Кнута-Морриса-Пратта O(n) ≤ 2H Один из первых алгоритмов с линейной оценкой в худшем случае. Модификация алгоритма Ахо-Корасик, строящая автомат неявно на основе префикс-функции.
Алгоритм Апостолико-Крошмора O(n) < H ≤1,5H
Алгоритм Shift-Or
Bitap-алгоритм
Двоичный алгоритм
O(n+σ) =H·ceil(n/w) Эффективен, если размер needle (в символах) не больше размера машинного слова (в битах, обозначен как w). Легко переделывается на приблизительный поиск, поиск нескольких строк.

Основанные на сравнении с конца

[править | править код]

В этом семействе алгоритмов needle движется по haystack слева направо, но сравнение этих строк друг с другом проводится справа налево. Сравнение справа налево позволяет в случае несовпадения сдвинуть needle не на одну позицию, а на несколько.

Название Предв. обработка Сложность Примечания
типичная макс.
Алгоритм Бойера — Мура O(n+σ) <H O(Hn) Стандартный алгоритм поиска подстроки в строке. Считается наиболее эффективным алгоритмом общего назначения.[3]
Алгоритм Чжу-Такаоки O(n+σ²) <H O(Hn) Алгоритм Бойера — Мура, оптимизированный под короткие алфавиты
Алгоритм Апостолико-Джанкарло O(n+σ) <H ≤1,5H Одна из первых попыток получить <H в типичном случае и O(H) в худшем. Очень сложен в реализации.
Турбо-алгоритм Бойера — Мура O(n+σ) <H ≤2H Один из наиболее эффективных алгоритмов, не дающих регрессии на «плохих» данных

Проводящие сравнение в необычном порядке

[править | править код]
Название Предв. обработка Сложность Примечания
типичная макс.
Непримитивный алгоритм const <H O(Hn) Простой алгоритм, сравнивающий второй символ, затем начиная с третьего в режиме «чёрного ящика», и, наконец, первый. При n[1]≠n[2][4] и несовпадении на второй-третьей стадии — сдвиг на 2 вправо.
Алгоритм Райты
Алгоритм Бойера — Мура — Хорспула — Райты
O(n+σ) <H O(Hn) Эмпирический алгоритм, оптимизированный под английские тексты. Сравнивает последний символ, потом первый, потом средний, потом все остальные; при несовпадении — сдвиг по Хорспулу.

Примечания

[править | править код]
  1. Brute force algorithm Архивная копия от 21 января 2009 на Wayback Machine (англ.)
  2. Horspool algorithm. Дата обращения: 2 мая 2013. Архивировано 29 августа 2012 года.
  3. Boyer-Moore algorithm. Дата обращения: 2 мая 2013. Архивировано 6 февраля 2013 года.
  4. Напомним, символы нумеруются с 1, как в Паскале.

Литература

[править | править код]
  • Гасфилд Д. Строки, деревья и последовательности в алгоритмах: Информатика и вычислительная биология = Algorithms on String, Trees, and Sequences. Computer Science and Computational Biology / Пер. с англ. И. В. Романовского. — 2-е изд. — СПб.: Невский Диалект, 2003. — 654 с. — ISBN 5-7940-0103-8, 5-94157-321-9, 0-521-58519-8.
  • Смит Б. Методы и алгоритмы вычислений на строках = Computing Patterns in Strings. — М.: Вильямс, 2006. — 496 с. — ISBN 5-8459-1081-1, 0-201-39839-7.
  • Окулов С. М. Алгоритмы обработки строк. — М.: Бином, 2013. — 255 с. — ISBN 978-5-9963016-2-1.