Вы находитесь на странице: 1из 24

Содержание

Введение…………………………………………………………..................................................3
1 Теоретические основы ……………………………………………………………………..….5
1.1 Исторические сведения…………………………………………………….……………......5
1.2 Обзор основных понятий……………………………………………….……………….......9
2 Алгоритмы поиска подстроки в строке………………………………………………………13
2.1 Виды алгоритмов поиска……………………………………………………………….……13
2.2 Реализация алгоритмов поиска……………………………………………………………...17
Заключение…………………………………………………………………….............................20
Список использованных источников………………………………………………………..….21
Приложение A. Алгоритм поиска подстроки в строке Робина-Карпа …………………….....22
Приложение B. Алгоритм поиска подстроки в строке Бойера-Мура ………………………..24

2
Введение
В современном мире информация представляет большую значимость в формировании
сообщества, а также страны. Непосредственно, системы поиска дают возможность пользователям
по одному слову либо сочетанию слов осуществлять отбор с невероятной правильностью. На
данный момент существуют также несколько систем, которые могут представить ряд вариантов
склонений введённого слова либо сочетания слов. Глобальные либо локальные сети в отсутствии
поисковых систем привели бы к тому, что пользователь обязан был бы помнить огромное число
интернет адресов, зачастую обладающие сокращёнными наименованиями. С введением разных
компьютерных технологий в процесс производства, становятся важными такие проблемы как:
сохранение крупных размеров сведений в носителях, скорость передачи, но кроме того
стремительный поиск необходимой информации. В генной инженерии поиск совпадений
строковых последовательностей применяется при анализе ДНК. В программировании - равно как в
одной из наиболее трудных областей компьютерной промышленности - благодаря поиску в
настоящее, время, возможно, отыскать погрешности, при лексическом рассмотрении
компьютерных проектов. Информация из разных баз данных, также делается недоступной для
извлечения в отсутствии надлежащего поиска согласно установленным характеристикам. Каждая
искательская концепция основывается в базе одного либо некоторых скомбинированных
алгоритмов. Любой метод выделяется собственной быстротой вычисления итога. Методы
вычисления строковых совпадений используются в таких сферах, как распознавание речи,
компьютерное зрение, криптография, сжатие данных, вычислительная геометрия также
молекулярная биология. Нереально вообразить всемирно знаменитую лингвистическую
концепцию ABBYY LINGVO в отсутствии стремительного поиска согласно словам, либо
концепцию WINDOWS в отсутствии способности поиска файлов. Важным условием оптимальной
поисковой концепции считается подсознательно-доступный интерфейс, что сможет помочь
пользователю отыскать требуемые предметы.
Целью этой курсовой работы является изучение основных алгоритмов поиска подстроки в
строковых последовательностях. В ходе достижения этой цели будут решены следующие задачи.
Первая заключается в изучении исторических сведений об алгоритмах. Без истории
невозможно представить себе развитие программирования, потому что на основе уже
разработанных алгоритмов, строятся новые или модернизируются уже существующие.
Программисты наших дней могут сделать множество качественных программ, только благодаря
огромному вкладу в это дело предыдущих поколений.
Вторая задача включает в себя выявление основных понятий, связанных с
программированием и алгоритмом поиска слов.

3
В третью задачу входит изучение архитектуры алгоритмов поиска текста в строке,
сравнение таких алгоритмов как:

 алгоритм прямого поиска;


 алгоритм Д. Кнута, Д. Мориса и В. Пратта (КМП-поиск);
 алгоритм Р. Боуера и Д. Мура (БМ-поиск);
 алгоритм Рабина-Карпа (РК-поиск)
и выявление положительных и отрицательных моментов в процессе вычисления.
Четвёртая задача включает в себя практические моменты применения алгоритмов поиска в
тексте. Будет рассматриваться реализация ряда алгоритмов напрямую связанных с решением
сегодняшних проблем.

4
1 Теоретические основы
В данной главе будут рассмотрены основные теоретические понятия
связанные с алгоритмами, алгоритмами поиска и алгоритмами поиска
подстроки в строке, и изучены исторические сведения связанные с ними.

1.1 Исторические сведения


Современное формальное определение алгоритма было дано в 30-50-е годы XX века в
работах Тьюринга, Поста, Чёрча, Н. Винера, А.А. Маркова.
Само слово «алгоритм» произошло от имени хорезмского учёного Абу Абдуллах
Мухаммеда ибн Муса аль-Хорезми. Около 825 года он написал сочинение, в котором впервые дал
описание придуманной в Индии позиционной десятичной системы счисления. Аль-Хорезми
сформулировал правила вычислений в новой системе и, впервые использовал цифру 0 для
обозначения пропущенной позиции в записи числа. Приблизительно в это же время индийские
цифры начали применять и другие арабские учёные. В первой половине XII века книга аль-
Хорезми в латинском переводе проникла в Европу. Переводчик, имя которого до нас не дошло,
дал ей название Algoritmi de numero Indorum («Алгоритмы о счёте индийском»). По-арабски же
книга именовалась Китаб аль-джебр валь-мукабала («Книга о сложении и вычитании»). Из
оригинального названия книги происходит слово Алгебра (алгебра - аль-джебр - восполнение).
В течение следующих столетий появилось множество других трудов, посвящённых всё
тому же вопросу - обучению искусству счёта с помощью цифр. В названии каждого труда было
слово algoritmi или algorismi. Про аль-Хорезми позднейшие авторы ничего не знали, но поскольку
первый перевод книги начинается словами: «Dixit algorizmi:» («Аль-Хорезми говорил:»), всё ещё
связывали это слово с именем конкретного человека. Очень распространённой была версия о
греческом происхождении книги. В англо-норманнской рукописи XIII века, написанной в стихах,
можно прочесть: “Алгоризм был придуман в Греции. Это часть арифметики. Придуман он был
мастером по имени Алгоризм, который дал ему своё имя. И поскольку его звали Алгоризм, Он
назвал свою книгу «Алгоризм»”.
Около 1250 года английский астроном и математик Иоанн Сакробоско написал труд по
арифметике Algorismus vulgaris, который стал на столетия основным учебником по вычислениям в
десятичной позиционной системе счисления в европейских университетах. Во введении
Сакробоско назвал автором науки о счёте мудреца по имени Алгус (Algus). В популярной
средневековой поэме «Роман о Розе» (1275-1280) Жана де Мена «греческий философ Алгус»
ставится в один ряд с Платоном, Аристотелем, Евклидом и Птолемеем.«Мастер Алгус» стал в
средневековой литературе олицетворением счётного искусства.

5
Со временем, слово algorism (или algorismus) обрело значение способа выполнения
арифметических действий посредством арабских цифр, то есть на бумаге, без использования
абака. Абак - счётная доска, применявшаяся для арифметических вычислений приблизительно с V
века до н. э. в Древней Греции, Древнем Риме. Многие века абак был фактически единственным
средством для практичных вычислений, им пользовались и купцы, и менялы, и учёные.
Достоинства вычислений на счётной доске разъяснял в своих сочинениях такой
выдающийся мыслитель, как Герберт Аврилакский, ставший в 999 г. папой римским под именем
Сильвестра II. Новое с огромным трудом пробивало себе дорогу, и в историю математики вошло
упорное противостояние лагерей алгорисмиков и абацистов (иногда называемых гербекистами),
которые пропагандировали использование для вычислений абака вместо арабских цифр. Прошло
не одно столетие, прежде чем новый способ счёта окончательно утвердился, столько времени
потребовалось, чтобы выработать общепризнанные обозначения, усовершенствовать и
приспособить к записи на бумаге методы вычислений. В Европе учителей арифметики вплоть до
XVII века продолжали называть «магистрами абака», а сочинения по искусству счёта назывались
Алгоритмами.
Постепенно значение слова расширялось. Учёные начинали применять его не только к
сугубо вычислительным, но и к другим математическим процедурам. Например, около 1360 г.
французский философ Николай Орем написал математический трактат Algorismus proportionum
(«Вычисление пропорций»), в котором впервые использовал степени с дробными показателями и
фактически вплотную подошёл к идее логарифмов. Когда же на смену абаку пришёл так
называемый счёт на линиях, многочисленные руководства по нему стали называть Algorithmus
linealis, то есть правила счёта на линиях.
Первоначальная форма algorismi спустя некоторое время потеряла последнюю букву, и
слово приобрело более удобное для европейского произношения вид algorism. Позднее и оно,
подверглось искажению, скорее всего, связанному со словом arithmetic.
В XVIII веке в одном из германских математических словарей, Vollstandiges mathematisches
Lexicon, термин algorithmus всё ещё объясняется как понятие о четырёх арифметических
операциях. Но такое значение не было единственным, ведь терминология математической науки в
те времена ещё только формировалась. В частности, выражение algorithmus infinitesimalis
применялось к способам выполнения действий с бесконечно малыми величинами.
Пользовался словом алгоритм и Леонард Эйлер, одна из работ которого так и называется -
«Использование нового алгоритма для решения проблемы Пелля».

a. [

6
b. https://ru.wikipedia.org/wiki/Алгоритм#История_термина ]

Потребовалось ещё почти два столетия, чтобы все старинные значения слова вышли из
употребления. Этот процесс можно проследить на примере проникновения слова «алгоритм» в
русский язык.
Историки датируют 1691 годом один из списков древнерусского учебника арифметики,
известного как «Счётная мудрость». Это сочинение известно во многих вариантах и восходит к
ещё более древним рукописям XVI века. По ним можно проследить, как знание арабских цифр и
правил действий с ними постепенно распространялось на Руси.
Полное название этого учебника - «Сия книга, глаголемая по-эллински и по-гречески
арифметика, а по-немецки алгоритма, а по-русски цифирная счётная мудрость».
Таким образом, слово «алгоритм» понималось первыми русскими математиками так же, как
и в Западной Европе. Однако его не было ни в знаменитом словаре В.И. Даля, ни спустя сто лет в
«Толковом словаре русского языка» под редакцией Д.Н. Ушакова. Но слово «алгоритм» можно
найти в популярном дореволюционном Энциклопедическом словаре братьев Гранат, и в первом
издании Большой советской энциклопедии. Здесь оно трактуется как правило, по которому
выполняется то или иное из четырёх арифметических действий в десятичной системе счисления.
Однако к началу XX в. для математиков слово «алгоритм» уже означало любой арифметический
или алгебраический процесс, выполняемый по строго определённым правилам, и это объяснение
также даётся в следующих изданиях БСЭ.
Алгоритмы становились предметом всё более пристального внимания учёных, и
постепенно это понятие заняло одно из центральных мест в современной математике. Что же
касается людей, от математики далёких, то к началу сороковых годов это слово они могли
услышать разве что во время учёбы в школе, в сочетании «алгоритм Евклида». Несмотря на это,
алгоритм всё ещё воспринимался как термин сугубо специальный, что подтверждается
отсутствием соответствующих статей в менее объёмных изданиях. В частности, его нет даже в
десятитомной Малой советской энциклопедии (1957 г.), не говоря уже об однотомных
энциклопедических словарях. Но зато спустя десять лет, в третьем издании Большой советской
энциклопедии (1969 г.) алгоритм уже характеризуется как одна из основных категорий
математики, «не обладающих формальным определением в терминах более простых понятий, и
абстрагируемых непосредственно из опыта». Как мы видим, отличие даже от трактовки первым
изданием БСЭ разительное! За сорок лет алгоритм превратился в одно из ключевых понятий
математики, и признанием этого стало включение слова уже не в энциклопедии, а в словари.
Например, оно присутствует в академическом «Словаре русского языка» (1981 г.) именно как
термин из области математики.

7
Одновременно с развитием понятия алгоритма постепенно происходила и его экспансия из
чистой математики в другие сферы. И начало ей положило появление компьютеров, благодаря
которому слово «алгоритм» вошло в 1985 г. во все школьные учебники информатики и обрело
новую жизнь. Вообще можно сказать, что его сегодняшняя известность напрямую связана со
степенью распространения компьютеров. Например, в третьем томе «Детской энциклопедии»
(1959 г.) о вычислительных машинах говорится немало, но они ещё не стали чем-то привычным и
воспринимаются скорее, как некий атрибут светлого, но достаточно далёкого будущего.
Соответственно и алгоритмы ни разу не упоминаются на её страницах. Но уже в начале 70-х гг.
прошлого столетия, когда компьютеры перестали быть экзотической диковинкой, слово
«алгоритм» стремительно входит в обиход. Это чутко фиксируют энциклопедические издания. В
«Энциклопедии кибернетики» (1974 г.) в статье «Алгоритм» он уже связывается с реализацией на
вычислительных машинах, а в «Советской военной энциклопедии» (1976 г.) даже появляется
отдельная статья «Алгоритм решения задачи на ЭВМ». За последние полтора-два десятилетия
компьютер стал неотъемлемым атрибутом нашей жизни, компьютерная лексика становится всё
более привычной. Слово «алгоритм» в наши дни известно, вероятно, каждому. Оно уверенно
шагнуло даже в разговорную речь, и сегодня мы нередко встречаем в газетах и слышим в
выступлениях политиков выражения вроде «алгоритм поведения», «алгоритм успеха» или даже
«алгоритм предательства».
В данной работе также будет использовано понятие «алгоритма поиска строки в
подстроке». В развитии этого алгоритма принимали участие многие учёные.
Ричард Карп в 1987 году, вместе с Майклом Рабином, разработал алгоритм поиска
подстроки, названный в их честь.
Ричард Карп родился в 1935 году в семье учителя математики и директора средней школы
Эйбрахама Луиса Карпа и его жены Розы Карп в Бостоне, штат Массачусетс. С ним росли двое
младших братьев Роберт и Дэвид, и младшая сестра Кэролин. Окончив школу, Ричард поступил в
Гарвардский университет, где получил титулы бакалавра (1955), магистра наук (1956) и наконец
доктора философии по прикладной математике в 1959 году. После учёбы работал 9 лет в
исследовательском центре IBM. В 1968 году он получил профессуру по информатике, математике
и исследованию операций при калифорнийском университете Беркли, где и работает по сей день,
не учитывая четырёхлетнего перерыва на работу в университете Вашингтона. В 1971 году Карп
вместе с Джэком Эдмондсом разработал алгоритм для нахождения максимального потока в
транспортной сети. Год спустя, Карп опубликовал свой труд «Reducibility Among Combinatorial
Problems», в котором он доказал NP-полноту для 21 задачи.
Михаэль Рабин родился в 1931 году сыном раввина Исраэля Аврахама Рабина в городе
Бреслау (ныне Вроцлав), принадлежащему тогда к Пруссии. В 1935 году его семья эмигрировала в

8
Палестину. В 1953 году он получил титул магистра наук, закончив учёбу в Еврейском
университете в Иерусалиме. Три года спустя, в 1956 году, защитил диссертацию в Принстонском
университете и стал доктором философии. В настоящее время (сентябрь 2008 года) Майкл Рабин
занимается исследованиями в области компьютерной безопасности и преподаёт в Иерусалиме и
Гарварде.
В 1977 году были опубликованы результаты работ Д. Кнута, В. Пратта, Д. Морриса также
занимавшихся алгоритмами поиска.
Дональд Эрвин Кнут - американский учёный, почётный профессор в отставке
Стэнфордского университета и нескольких других университетов в разных странах, иностранный
член Российской академии наук, преподаватель и идеолог программирования, автор 19
монографий и более 160 статей, разработчик нескольких известных программных технологий.
Автор всемирно известной серии книг, посвящённой основным алгоритмам и методам
вычислительной математики, а также создатель настольных издательских систем TEX и
METAFONT, предназначенных для набора и вёрстки книг, посвящённых технической тематике.
Родился 10 января 1938 в США в семье преподавателя. Отец Дональда преподавал бухгалтерский
учёт, а также занимался печатным делом на дому как любитель. С юных лет в Дональде
наблюдалась склонность к математике, физике и музыке. Профессор Кнут удостоен премии
Тьюринга (1974), Национальная научная медаль США (1979) и AMS Steele Prize, премия Харви
(1995 год), премия Киото (1996) за достижения в области передовых технологий, премия имени
Грейс Мюррей Хоппер (1971).
Наиболее быстрый алгоритм среди алгоритмов общего назначения, предназначенных для
поиска подстроки в строке, был разработан Робертом Бойером и Джеем Муром в 1977 году.
Преимущество этого алгоритма в том, что ценой некоторого количества предварительных
вычислений над шаблоном, шаблон сравнивается с исходным текстом не во всех позициях - часть
проверок пропускаются как заведомо не дающие результата.

1.2 Обзор основных понятий


Алгоритм - набор инструкций, описывающих последовательность операций исполнителя с
целью достижения результата решения задачи за окончательное количество операций. В прежней
трактовке взамен фразы «порядок» применялся термин «последовательность», однако согласно
мере формирования параллельности в работе компьютеров термин «последовательность» начали
замещать наиболее общим словом «порядок». Данное связано с тем, что деятельность каких-то
инструкций алгоритма может быть зависима от иных инструкций либо результатов их работы.
Подобным способом, некоторые инструкции обязаны осуществляться точно уже после окончания
работы инструкций, от которых они зависят. Независимые инструкции либо инструкции, ставшие

9
самостоятельными из-за окончания работы инструкций, от которых они находятся в зависимости,
имеют все шансы осуществляться в случайном режиме, параллельно либо одновременно, в случае
если это позволяют применяемые процессор и операционная система. Зачастую в качестве
исполнителя выступает некоторый механизм (компьютер, токарный станок, швейная машина),
однако представление метода необязательно относится к компьютерным программам, таким
образом, например, отчетливо описанный способ изготовления блюда, кроме того, считается
методом, в этом случае исполнителем считается индивид. Представление метода принадлежит к
первоначальным, основным, базовым понятиям математики. Вычислительные движения
алгоритмического характера (арифметические действия над целыми числами, нахождение
максимального общего делителя 2-ух чисел и т. д.) знакомы людям с глубочайшей древности. Но,
в очевидном варианте представление метода сложилось только в начале XX столетия. В
современном обществе метод в формализованном выражении составляет основу образования в
случаях, согласно подобию.
Существуют основные свойства алгоритма, которыми должен обладать любой из них:
- Детерминированность (определенность). Это свойство заключается в том, что при задании
одних и тех же исходных данных несколько раз алгоритм будет выполняться абсолютно
одинаково и всегда будет получен один и тот же результат. Свойство детерминированности
проявляется также и в том, что на каждом шаге выполнения алгоритма всегда точно известно, что
делать дальше, а каждое действие однозначно понятно исполнителю и не может быть истолковано
неопределенно. Благодаря этому свойству выполнение алгоритма носит механический характер.
- Массовость - выражается в том, что с помощью алгоритма можно решать не одну
конкретную задачу, а любую задачу из некоторого класса однотипных задач при всех допустимых
значениях исходных данных.
- Результативность - означает, что выполнение алгоритма обязательно должно привести к
решению поставленной задачи, либо к сообщению о том, что при заданных исходных величинах
задачу решить невозможно. Алгоритмический процесс не может обрываться безрезультатно.
- Дискретность - означает, что алгоритм состоит из последовательности отдельных шагов -
элементарных действий, выполнение которых не представляет сложности. Именно благодаря
этому свойству алгоритм может быть реализован на ЭВМ.
- Конечность - заключается в том, что последовательность элементарных действий
алгоритма не может быть бесконечной, неограниченной, хотя может быть очень большой (если
требуется, например, большая точность вычислений).
- Корректность - означает, что если алгоритм создан для решения определенной задачи, то
для всех исходных данных он должен всегда давать правильный результат и ни для каких
исходных данных не будет получен неправильный результат.

10
[ 2. Билл Смит. Методы и алгоритмы вычислений на строках. М.: Вильямс, 2006]

Если хотя бы один из полученных результатов противоречит хотя бы одному из ранее


установленных и получивших признание фактов, алгоритм нельзя признать корректным.
Если последовательность действий не обладает хотя бы одним из перечисленных выше
свойств, то она не может считаться алгоритмом.
Кроме основных свойств хорошие строковые алгоритмы должны обладать следующими
качествами, которые определят их практическую полезность:
- Корректность.
- Скорость вычислений и требуемая память (временная и пространственная сложность
алгоритма).
- Отсутствие возврата к ранее просмотренным данным.
- Независимость от алфавита.
- Возможность предварительной подготовки данных.
- Кодирование выходного результата.
Корректность является необходимым свойством любого алгоритма, - некорректный
алгоритм бесполезен. В худшем случае некорректный алгоритм, реализованный в компьютерной
программе, может быть просто опасным, если, например, он управляет тормозной системой
автомобиля или полетом самолета. Доказательство корректности программных продуктов - даже
если есть четкое определение понятия корректности - большая нерешенная проблема "инженерии
программного обеспечения". Доказательство корректности алгоритма проходит два основных
этапа. На первом этапе предлагается математическое "доказательство" корректности; на втором
этапе выполняется проверка алгоритма на тестовых входных данных, для которых известен
правильный выходной результат. Доказательства корректности, выполненные на каждом из этих
этапов, ненадежны. Математические доказательства ненадежны из-за "человеческого фактора", а
выполнение алгоритма на тестовых данных не может доказать "полную" корректность, поскольку
тестовые данные никогда не бывают исчерпывающими. Однако лучше иметь ненадежные
средства, чем не иметь средств вообще.
Временная и пространственная сложность - основные характеристики любого алгоритма, и,
конечно, алгоритм, время выполнения которого в наихудшем случае имеет порядок θ(n) на
строках длиной n, предпочтительнее алгоритма с временем выполнения в наихудшем случае
порядка θ(n2). Аналогично тщательно разработанные алгоритмы сравнения с паттернами, где
сведено к теоретическому минимуму количество парных буквенных сравнений, почти не
используются на практике, поскольку они чрезвычайно сложны, вследствие чего усложняется

11
процесс вычисления, что, в свою очередь, приводит к увеличению значений констант
пропорциональности в их асимптотических оценках.
[ https://ru.wikipedia.org/wiki/Алгоритм#Свойства_алгоритмов ]

Если сравнивать временную и пространственную сложности алгоритмов, то временная


сложность считается более важным критерием при выборе алгоритма.
Это отображает тот факт, что стоимость хранения единицы данных обычно ниже стоимости
единицы времени процесса вычислений. Таким образом, временная сложность алгоритма часто
"перевешивает" все другие характеристики алгоритма.
Отсутствие возврата к ранее просмотренным данным:
Просмотр строковых последовательностей, естественно, выполнять слева направо. Поэтому
чрезвычайно привлекательным свойством строковых алгоритмов считается их «онлайновость».
Это означает, что если в процессе вычислений уже просмотрена позиция «i» в строковой
последовательности, то алгоритм уже не вернется к этой позиции «i» снова. Другими словами,
онлайновый алгоритм никогда не выполняет возврата к уже просмотренным позициям.
Независимость от алфавита
Cтроковые последовательности в общем случае могут быть определены на произвольных
множествах элементов. Поэтому желательно, чтобы строковые алгоритмы эффективно
выполнялись на строках, определенных на любых алфавитах, то есть, чтобы они были независимы
от алфавита. Часто можно уменьшить временную сложность алгоритма, если использовать
некоторые особые свойства алфавита.
Необходимость предварительной подготовки данных
Вычисление дерева суффиксов можно рассматривать как процесс предварительной
подготовки данной строки х: это дерево затем используется для многократных поисков в строке
«х», при этом для каждого такого поиска его время пропорционально длине искомого паттерна.
Алгоритм, использующий предварительную подготовку, во многих случаях предпочтительнее
других, поскольку предполагает, что алфавит обработан заранее. Алгоритмы для подсчета
количества вхождений букв в заданные строки в большинстве случаев, являются онлайновыми.
Кодирование выходного результата
При вычислении кратных строк ключевую роль играет кодирование (способ представления)
выходного результата, которое позволяет уменьшить объем выходных данных, а также сложность
алгоритма.

12
[https://neerc.ifmo.ru/wiki/index.php?title=Поиск_подстроки_в_строке ]

2 Алгоритмы поиска подстроки в строке


В данной главе будут рассмотрены виды алгоритмов поиска подстроки в строке, их
характеристика по различным позициям и сравнение, а так же реализация двух из них.

2.1 Виды алгоритмов поиска

Часто приходится сталкиваться со специфическим поиском, так называемым поиском


строки (поиском в строке). Пусть есть некоторый текст Т и слово (или образ) W. Необходимо
найти первое вхождение этого слова в указанном тексте. Это действие типично для любых систем
обработки текстов. (Элементы массивов Т и W – символы некоторого конечного алфавита –
например, {0, 1}, или {a, …, z}, или {а, …, я}.) Наиболее типичным приложением такой задачи
является документальный поиск: задан фонд документов, состоящих из последовательности
библиографических ссылок, каждая ссылка сопровождается «дескриптором», указывающим тему
соответствующей ссылки. Надо найти некоторые ключевые слова, встречающиеся среди
дескрипторов. Мог бы иметь место, например, запрос «Программирование» и «Java». Такой запрос
можно трактовать следующим образом: существуют ли статьи, обладающие дескрипторами
«Программирование» и «Java».
Поиск строки формально определяется следующим образом. Пусть задан массив Т из N
элементов и массив W из M элементов, причем 0<M≤N. Поиск строки обнаруживает первое
вхождение W в Т, результатом будем считать индекс i, указывающий на первое с начала строки (с
начала массива Т) совпадение с образом (словом).
Пример. Требуется найти все вхождения образца W = abaa в текст T=abcabaabcabca.

Рис.1 Поиск подстроки в строке.


Образец входит в текст только один раз, со сдвигом S=3, индекс i=4.
Алгоритм прямого поиска
Идея алгоритма:
1. I=1,
2. сравнить I-й символ массива T с первым символом массива W,

13
3. совпадение → сравнить вторые символы и так далее,
4. несовпадение → I:=I+1 и переход на пункт 2,
[ https://habr.com/ru/post/111449/ ]

Условие окончания алгоритма:


1. подряд М сравнений удачны,
2. I+M>N, то есть слово не найдено.
Сложность алгоритма:
Худший случай. Пусть массив T→{AAA….AAAB}, длина │T│=N, образец W→{A….AB},
длина │W│=M. Очевидно, что для обнаружения совпадения в конце строки потребуется
произвести порядка N*M сравнений, то есть O(N*M).
Недостатки алгоритма:

 высокая сложность — O(N*M), в худшем случае – Θ((N-M+1)*M);


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

Алгоритм Д. Кнута, Д. Мориса и В. Пратта (КМП-поиск)


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

Рис.2 Пример алгоритма поиска Кнута-Мориса-Пратта

После частичного совпадения начальной части образа W с соответствующими символами


строки Т мы фактически знаем пройденную часть строки и может «вычислить» некоторые
сведения (на основе самого образа W), с помощью которых потом быстро продвинемся по тексту.
Идея КМП-поиска – при каждом несовпадении двух символов текста и образа образ
сдвигается на все пройденное расстояние, так как меньшие сдвиги не могут привести к полному
совпадению.

14
Особенности КМП-поиска:

 требуется порядка (N+M) сравнений символов для получения результата;


[1. Кнут Д. Алгоритм Кнута-Морриса-Пратта ]

 схема КМП-поиска дает подлинный выигрыш только тогда, когда неудаче


предшествовало некоторое число совпадений. Лишь в этом случае образ сдвигается
более чем на единицу. К несчастью, совпадения встречаются значительно реже чем
несовпадения. Поэтому выигрыш от КМП-поиска в большинстве случаев текстов весьма
незначителен.
Алгоритм Р. Боуера и Д. Мура (БМ-поиск)
На практике алгоритм БМ-поиска наиболее эффективен, если образец W длинный, а
мощность алфавита достаточно велика. Идея БМ-поиска – сравнение символов начинается с конца
образца, а не с начала, то есть сравнение отдельных символов происходит справа налево. Затем с
помощью некоторой эвристической процедуры вычисляется величина сдвига вправо s. И снова
производится сравнение символов, начиная с конца образца. Этот метод не только улучшает
обработку самого плохого случая, но и даёт выигрыш в промежуточных ситуациях. Почти всегда,
кроме специально построенных примеров, БМ-поиск требует значительно меньше N сравнений. В
самых же благоприятных обстоятельствах, когда последний символ образца всегда попадает на
несовпадающий символ текста, число сравнений равно (N / M), в худшем же случае – О((N-
M+1)*M+ p), где p – мощность алфавита.

Алгоритм Рабина-Карпа (РК-поиск)


Пусть алфавит D={0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, то есть каждый символ в алфавите есть d–ичная
цифра, где d=│D│.
Пример. Пусть образец имеет вид W = 3 1 4 1 5
Вычисляем значения чисел из окна длины |W|=5 по mod q, q — простое число.

Рис.3 Пример алгоритма Рабина-Карпа


23590(mod 13)=8, 35902(mod 13)=9, 59023(mod 13)=9,

k1=314157(mod 13) – вхождение образца,

k2=673997(mod 13) – холостое срабатывание.

15
Из равенства ki= kj (mod q) не следует, что ki= kj (например, 31415=67399(mod 13), но это
не значит, что 31415=67399). Если ki= kj (mod q), то ещё надо проверить, совпадают ли строки
W[1…m] и T[s+1…s+m] на самом деле.
Если простое число q достаточно велико, то дополнительные затраты на анализ холостых
срабатываний будут невелики. В худшем случае время работы алгоритма РК — Θ((N-M+1)*M), в
среднем же он работает достаточно быстро – за время О(N+M).
Пример: Сколько холостых срабатываний k сделает алгоритм РК, если
q= 11, 13, 17. Пусть W={2 6}

Рис.4 Пример алгоритма Рабина-Карпа

26 mod 11=4 → k =3 холостых срабатывания,


26 mod 13=0 → k =1 холостое срабатывание,
26 mod 17=9 → k =0 холостых срабатываний.
Очевидно, что количество холостых срабатываний k является функцией от величины
простого числа q (если функция обработки образца mod q) и, в общем случае, от вида функции для
обработки образца W и текста Т.
Таблица сравнение алгоритмов
 |Σ|=σ|Σ|=σ — размер алфавита
 |text|=t|text|=t — длина текста
 |pattern|=p|pattern|=p — длина паттерна
 aa — размер ответа (количество пар)
 mm — суммарная длина всех паттернов

16
Количество
Порядок
Название Среднее Худшее Препроцессинг Доп. память поисков. Описание
сравнен.
шаблонов

Данный алгоритм
использует
Алгоритм хэширование, что
Рабина- O(p+t)O( O(pt)O(p Single / снижает скорость в
O(p)O(p) O(1)O(1) Прямой
Карпа p+t) t) Finite среднем. Можно
модифицировать для
поиска нескольких
паттернов

Алгоритм
Кнута- O(p+t)O( O(p+t)O( Использует префикс-
O(p)O(p) O(p)O(p) Single Прямой
Морриса- p+t) p+t) функцию
Пратта

Считается наиболее
быстрым из
Алгоритм алгоритмов общего
Бойера- O(pt)O(p O(p+σ)O(p+ назначения.
O(t)O(t) O(p+σ)O(p+σ) Single Обратный
Мура t) σ) Использует эвристики.
Существует большое
количество
оптимизаций

2.2 Реализация алгоритмов поиска


Для реализации практической части были выбраны следующие алгоритмы поиска
подстроки в строке:

Алгоритм Рабина-Карпа, реализация на С++ :


int RabinKarpMatch(char *T,char *P,int d,int q)
{ int i,j,p,t,n,m,h,found;
n = strlen(T);
m = strlen(P);
h = mod(d,m-1,q);
p = t = 0;
for (i=0; i<m; i++)
{ p = (d*p + tonum(P[i])) % q;
t = (d*t + tonum(T[i])) % q; }
for (i=0; i<=n; i++)
{ if (p == t)
{ found = 1;
for (j=0; j<m; j++)

17
if (P[j] != T[i+j])
{ found = 0;
break; }
if (found)
return i+1; }
else
{
t = (d*(t - ((tonum(T[i])*h) % q)) + tonum(T[i+m])) % q;
}
}
return -1;
}

Вывод программы на С++

Программа нашла местоположение 1 символа из необходимой нам подстроки и указала его


положение в строке.
Алгоритм Бойера-Мура реализация на С++:
int BMSearch(char *string, char *substring){
int sl = 0;
int ssl = 0;
int res = -1;
while (string[sl] != NULL) {
sl++; }
while (substring[ssl] != NULL) {
ssl++; }
if (sl == 0)
printf("wrong string\n");
else if (ssl == 0)
printf("wrong substring\n");
else {
int i, Pos;
int BMT[256];
for (i = 0; i < 256; i ++)
BMT[i] = ssl;
for (i = ssl-1; i >= 0; i--)
if (BMT[(short)(substring[i])] == ssl)

18
BMT[(short)(substring[i])] = ssl - i - 1;
Pos = ssl - 1 ;
while (Pos < sl)
if (substring[ssl - 1 ] != string[Pos])
Pos = Pos + BMT[(short)(string[Pos])];
else
for (i = ssl - 2; i >= 0; i--){
if (substring[i] != string[Pos - ssl + i +1 ]) { //
Pos += BMT[(short)(string[Pos - ssl + i + 1])]-1;//
break; }
else
if (i == 0)
return Pos - ssl + 1; }
}
printf("\n");
return res; }

Вывод программы на С++

Программа нашла местоположение 1 символа из необходимой нам подстроки и указала его


положение в строке.

19
Заключение
В ходе выполнения данной курсовой работы были выполнены все поставленные задачи:
изучены исторические сведения об алгоритмах; выявлены основные понятия, связанные с
программированием и алгоритмом поиска слов; изучены архитектуры алгоритмов поиска
подстроки в строке и было проведено сравнение таких алгоритмов как: алгоритм прямого поиска;
алгоритм Д. Кнута, Д. Мориса и В. Пратта (КМП-поиск); алгоритм Р. Боуера и Д. Мура (БМ-
поиск); алгоритм Рабина-Карпа (РК-поиск). Так же выявлены положительные и отрицательные
моменты в процессе вычисления и реализованы алгоритмы поиска подстроки в строке на примере
двух алгоритмов: Рабина-Карпа и Бойера-Мура.

20
Список использованных источников
2. Кнут Д. Алгоритм Кнута-Морриса-Пратта
3. Билл Смит. Методы и алгоритмы вычислений на строках. М.: Вильямс, 2006..
4. Стивенс Р. Delphi. Готовые алгоритмы. Учебное пособие.
[Электронный ресурс]. – Режим доступа:
5. https://ru.wikipedia.org/wiki/Алгоритм#Свойства_алгоритмов

a. https://ru.wikipedia.org/wiki/Алгоритм#История_термина
6. https://habr.com/ru/post/111449/
7. https://neerc.ifmo.ru/wiki/index.php?title=Поиск_подстроки_в_строке

21
Приложение A.

Алгоритм поиска подстроки в строке Робина-Карпа


#include <stdio.h>

#include <string.h>

#include <math.h>

#include <iostream>

using namespace std;

#define tonum(c) (c >= 'A' && c <= 'Z' ? c - 'A' : c - 'a' + 26)

int mod(int a,int p,int m)

{ if (p == 0)

return 1;

int sqr = mod(a,p/2,m) % m;

if (p & 1)

return ((a % m) * sqr) % m;

else

return sqr; }

int RabinKarpMatch(char *T,char *P,int d,int q)

{ int i,j,p,t,n,m,h,found;

n = strlen(T);

m = strlen(P);

h = mod(d,m-1,q);

p = t = 0;

for (i=0; i<m; i++)

{ p = (d*p + tonum(P[i])) % q;

t = (d*t + tonum(T[i])) % q; }

for (i=0; i<=n; i++)

{ if (p == t)

{ found = 1;

for (j=0; j<m; j++)

if (P[j] != T[i+j])

{ found = 0;

break; }

22
if (found)

return i+1; }

else

{ t = (d*(t - ((tonum(T[i])*h) % q)) + tonum(T[i+m])) % q;

return -1; }

int main(int argc, char* argv[])

{ int sovp;

int d=1, q=1000;

char T[200];

char P[100];

cout<<"Enter the string:"<<endl;

cin >> T;

cout<<"Enter the search substring in the string:"<<endl;

cin >> P;

sovp= RabinKarpMatch(T,P,d, q);

if(sovp)

cout<<"The substring is find in position "<<sovp<<" ";

else

cout<<"Not found";

system("pause");

return 0; }

23
Приложение B.

Алгоритм поиска подстроки в строке Бойера-Мура


#include <stdio.h>

#include <stdlib.h>

#include <locale.h>

int BMSearch(char *string, char *substring){

int sl = 0;

int ssl = 0;

int res = -1;

while (string[sl] != NULL) {

sl++; }

while (substring[ssl] != NULL) {

ssl++; }

if (sl == 0)

printf("wrong string\n");

else if (ssl == 0)

printf("wrong substring\n");

else {

int i, Pos;

int BMT[256];

for (i = 0; i < 256; i ++)

BMT[i] = ssl;

for (i = ssl-1; i >= 0; i--)

if (BMT[(short)(substring[i])] == ssl)

BMT[(short)(substring[i])] = ssl - i - 1;

Pos = ssl - 1 ;

while (Pos < sl)

if (substring[ssl - 1 ] != string[Pos])

Pos = Pos + BMT[(short)(string[Pos])];

else

for (i = ssl - 2; i >= 0; i--){

if (substring[i] != string[Pos - ssl + i +1 ]) { //

Pos += BMT[(short)(string[Pos - ssl + i + 1])]-1;//

24
break; }

else

if (i == 0)

return Pos - ssl + 1; }

printf("\n");

return res; }

int main(int argc, char *argv[]) {

char str[30];

char substr[30];

printf("Enter the string: \n");

scanf("%s", str);

printf("Enter the search substring in the string: \n ");

scanf("%s", substr);

int pos = BMSearch(str, substr);

printf("The substring is find in position=%d", pos + 1 );

return 0; }

25

Вам также может понравиться