- •Билет 1. Концепция языка Си, его достоинства и недостатки
- •Билет 2. Представление целых чисел в эвм. Основные типы данных в Си и операции над ними. Особенности операций над вещественными числами.
- •Билет 3. Основные управляющие конструкции в Си. Понятие инварианта цикла и его применение.
- •Билет 4. Характеристика алгоритмов, программ, языков программирования и соответствия между ними.
- •Билет 5. Массивы и указатели в языке Си. Их представление, связь между ними.
- •Билет 6. Метод барьера и различные варианты его применения.
- •Билет 7. Процедуры и функции в Си. Формальные и фактические. Входные и выходные параметры, локальные и глобальные переменные.
- •Билет 8. Массивы и указатели в Си, связь между ними. Передача массивов и указателей как параметров процедур.
- •Билет 9. Представление строк и символов в Си. Операции над ними.
- •Билет 10. Понятие эффективности алгоритма, различные способы ее измерения.
- •Билет 11. Понятие рекурсии. Виды и характеристики.
- •Билет 12. Алгоритмы перебора с возвратом. Способы сокращения перебора.
- •Способы сокращения перебора
- •Билет 13. Алгоритмы оптимального поиска. Метод ветвей и границ.
- •Билет 14. Сортировка массивов. Простые методы.
- •Билет 15. Сортировка массивов. Усовершенствованные методы.
- •Билет 16. Алгоритм поиска подстроки в строке.
- •3. Эвристика совпавшего суффикса. Если при сравнении строки и шаблона совпало один или
- •Билет 17. Алгоритм получения случайных чисел. Правильный выбор параметров линейного конгруэнтного генератора.
Билет 16. Алгоритм поиска подстроки в строке.
Алгоритм Бойера — Мура поиска строки считается наиболее быстрым среди алгоритмов общего
назначения, предназначенных для поиска подстроки в строке. Был разработан Робертом Бойером
(англ. Robert S. Boyer) и Джеем Муром (англ. J Strother Moore) в 1977 году[1]. Преимущество
этого алгоритма в том, что ценой некоторого количества предварительных вычислений над
шаблоном (но не над строкой, в которой ведётся поиск) шаблон сравнивается с исходным
текстом не во всех позициях — часть проверок пропускаются как заведомо не дающие результата.
Общая оценка вычислительной сложности алгоритма — O(|haystack|+|needle|+|?|) на
непериодических шаблонах и O(|haystack|·|needle|+|?|) на периодических, где
haystack — строка, в которой выполняется поиск, needle — шаблон поиска, ? — алфавит,
на котором проводится сравнение. В 1991 году Коул показал, что на непериодических шаблонах
за полный проход по строке алгоритм совершит не более 3·|haystack| сравнений[2].
Описание алгоритма
Алгоритм основан на трёх идеях.
1. Сканирование слева направо, сравнение справа налево. Совмещается начало текста (строки)
и шаблона, проверка начинается с последнего символа шаблона. Если символы совпадают,
производится сравнение предпоследнего символа шаблона и т. д. Если все символы шаблона
совпали с наложенными символами строки, значит, подстрока найдена, и поиск окончен.
Если же какой-то символ шаблона не совпадает с соответствующим символом строки, шаблон
сдвигается на несколько символов вправо, и проверка снова начинается с последнего символа.
Эти «несколько», упомянутые в предыдущем абзаце, вычисляются по двум эвристикам.
2. Эвристика стоп-символа. Предположим, что мы производим поиск слова «колокол». Первая же
буква не совпала — «к» (назовём эту букву стоп-символом). Тогда можно сдвинуть шаблон вправо
до последней буквы «к».
!
Строка: * * * * * * к * * * * * *
Шаблон: к о л о к о л
Следующий шаг: к о л о к о л
Если стоп-символа в шаблоне вообще нет, шаблон смещается за этот стоп-символ.
!
Строка: * * * * * а л * * * * * * * *
Шаблон: к о л о к о л
Следующий шаг: к о л о к о л
В данном случае стоп-символ — «а», и шаблон сдвигается так, чтобы он оказался прямо
за этой буквой. В алгоритме Бойера-Мура эвристика стоп-символа вообще не смотрит на
совпавший суффикс (см. ниже), так что первая буква шаблона («к») окажется под «л», и
будет проведена одна заведомо холостая проверка.
Если стоп-символ «к» оказался за другой буквой «к», эвристика стоп-символа не работает.
!
Строка: * * * * к к о л * * * * *
Шаблон: к о л о к о л
Следующий шаг: к о л о к о л ?????
В таких ситуациях выручает третья идея АБМ — эвристика совпавшего суффикса.
3. Эвристика совпавшего суффикса. Если при сравнении строки и шаблона совпало один или
больше символов, шаблон сдвигается в зависимости от того, какой суффикс совпал.
Строка: * * т о к о л * * * * *
Шаблон: к о л о к о л
Следующий шаг: к о л о к о л
В данном случае совпал суффикс «окол», и шаблон сдвигается вправо до ближайшего «окол».
Если подстроки «окол» в шаблоне больше нет, но он начинается на «кол», сдвигается до «кол»,
и т. д.
Обе эвристики требуют предварительных вычислений — в зависимости от шаблона поиска
заполняются две таблицы. Таблица стоп-символов по размеру соответствует алфавиту
(например, если алфавит состоит из 256 символов, то её длина 256); таблица суффиксов —
искомому шаблону. Именно из-за этого алгоритм Бойера-Мура не учитывает совпавший суффикс
и несовпавший символ одновременно — это потребовало бы слишком много предварительных
вычислений.
Опишем подробнее обе таблицы.
[править]Таблица стоп-символов
Считается, что символы строк нумеруются с 1 (как в Паскале).
В таблице стоп-символов указывается последняя позиция в needle
(исключая последнюю букву) каждого из символов алфавита. Для всех символов, не вошедших в
needle, пишем 0 (для нумерации с 0 — соответственно, ?1). Например, если needle=«abcdadcd»,
таблица стоп-символов будет выглядеть так.
Символ a b c d [все остальные]
Последняя позиция 5 2 7 6 0
Обратите внимание, для стоп-символа «d» последняя позиция будет 6, а не 8 — последняя
буква не учитывается. Это известная ошибка, приводящая к неоптимальности. Для АБМ она
не фатальна («вытягивает» эвристика суффикса), но фатальна для упрощённой версии АБМ —
алгоритма Хорспула.
Если несовпадение произошло на позиции i, а стоп-символ c, то сдвиг будет i-StopTable[c].
Таблица суффиксов
Для каждого возможного суффикса S шаблона needle указываем наименьшую величину, на
которую нужно сдвинуть вправо шаблон, чтобы он снова совпал с S. Если такой сдвиг
невозможен, ставится |needle| (в обеих системах нумерации). Например, для того же
needle=«abcdadcd» будет:
Суффикс [пустой] d cd dcd ... abcdadcd
Сдвиг 1 2 4 8 ... 8
Иллюстрация
было ? ?d ?cd ?dcd ... abcdadcd
стало abcdadcd abcdadcd abcdadcd abcdadcd ... abcdadcd
Если шаблон начинается и заканчивается одной и той же комбинацией букв, |needle| вообще не
появится в таблице. Например, для needle=«колокол» для всех суффиксов (кроме, естественно,
пустого) сдвиг будет равен 4.
Суффикс [пустой] л ол ... олокол колокол
Сдвиг 1 4 4 ... 4 4
Иллюстрация
было ? ?л ?ол ... ?олокол колокол
стало колокол колокол колокол ... колокол колокол
Существует быстрый алгоритм вычисления таблицы суффиксов. Этот алгоритм использует
префикс-функцию строки.
m = length(needle)
pi[] = префикс-функция(needle)
pi1[] = префикс-функция(обращение(needle))
for j=0..m
suffshift[j] = m - pi[m]
for i=1..m
j = m - pi1[i]
suffshift[j] = min(suffshift[j], i - pi1[i])
Здесь suffshift[0] соответствует всей совпавшей строке; suffshift[m] — пустому суффиксу.
Поскольку префикс-функция вычисляется за O(|needle|) операций, вычислительная сложность
этого шага также равняется O(|needle|).