Добавил:
baraevaliza
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Лабы по ПиАА 2024 / Baraeva_Elizaveta_lb4
.pdf
МИНОБРНАУКИ РОССИИ
САНКТ-ПЕТЕРБУРГСКИЙ ГОСУДАРСТВЕННЫЙ
ЭЛЕКТРОТЕХНИЧЕСКИЙ УНИВЕРСИТЕТ
«ЛЭТИ» ИМ. В. И. УЛЬЯНОВА (ЛЕНИНА)
Кафедра МО ЭВМ
ОТЧЕТ
по лабораторной работе №4
по дисциплине «Построение и анализ алгоритмов»
Тема: Алгоритм Кнута-Морриса-Пратта
Студентка гр. 2383
Бараева Е. Н.
Преподаватель
Шевелева А. М.
Санкт-Петербург
2024

2
Цель работы.
Изучить алгоритм Кнут-Моррис-Пратта для поиска подстроки в строке,
и применить его для решения задачи об определении того, что является ли
одна строка циклическим сдвигом другой.
Задание.
1) Реализуйте алгоритм КМП и с его помощью для заданных шаблона P
(∣P∣≤15000) и текста T (∣T∣≤5000000) найдите все вхождения P в T.
Вход:
Первая строка - P
Вторая строка - T
Выход:
индексы начал вхождений P в T, разделенных запятой, если P не входит в
T, то вывести −1
Sample Input:
ab
abab
Sample Output:
0,2
2) Заданы две строки A (∣A∣≤5000000) и B(∣B∣≤5000000).
Определить, является ли А циклическим сдвигом В (это значит, что А и
В имеют одинаковую длину и А состоит из суффикса В, склеенного с
префиксом В). Например, defabc является циклическим сдвигом abcdef.
Вход:
Первая строка - A
Вторая строка - B
Выход:
Если A является циклическим сдвигом B, индекс начала строки B в A,
иначе вывести −1. Если возможно несколько сдвигов вывести первый индекс.
Sample Input:

3
defabc
abcdef
Sample Output:
3
Исследование: Анализ времени работы от длины строки и длины
подстроки.
Выполнение работы.
• prefix_function() – функция, которая вычисляет префикс-функцию для
заданного шаблона pattern. В начале инициализируется pattern_size
размером pattern, k нулем, prefix_values массивом нулей. Переменная k
будет использоваться для отслеживания текущего значения префиксфункции. В prefix_values будут храниться значения префикс-функции
для каждой позиции в строке. Далее в цикле сравниваются символы
строки, если они равны, то k увеличивается на 1 и его значение
записывается в массив. Также в цикле сравниваются символы pattern[k]
и pattern[i], если они не равны, то в k записывается значение из
предыдущей позиции prefix_values. В конке возвращается список
prefix_values.
• kmp_search() – функция алгоритма Кнут-Моррис-Пратта. В начале
инициализируется pattern_size, k также, как и в prefix_function.
prefix_values инициализируется значением префикс-функции. Еще
инициализируется пустой список result, в который будут добавляться
индексы начала вхождений pattern в text. Далее цикл такой же, как и в
prefix_function, но добавляется еще одно условие. В нем проверяется:
если k достигает размера подстроки, то это означает, что нашлось
вхождение подстроки в строку. Затем добавляется индекс начала
вхождения в список result и обновляется значение k с помощью
префикс-функции для последнего символа подстроки. В конце

4
возвращается список result, или -1, если ни одного вхождения не
найдено.
• В блоке if name == "main" производится ввод шаблона и текста, вызов
функции kmp_search(), и вывод индексов начала вхождений шаблона в
текст.
• is_cyclic_shift() – функция для 2 задания, которая сравнивает длины
подстроки и текста. Если они не равны, то возвращается -1, если равны,
то подстрока удваивается, и функция возвращает вызов kmp_search().
Тестирование.
Результаты тестирования представлены в табл. 1.
Таблица 1 – Результаты тестирования
№ п/п
Входные данные
Выходные
данные
Комментарии
1.
fk
fkhkhskfsdjfksjfksjhfjsdhfkhsdskjds
0,11,15,25
Задание 1.
Верно.
2.
asdfghjkl
hjklasdfg
5
Задание 2.
Верно.
3.
ewrtyuiopoiuy
ewrtyuiopoiuqrwrtyuiopoiuy
-1
Задание 1.
Верно.
4.
mnbvcxzlkjhgfdsapoiuytrewq
vcxzlkjhgfdsapoiuytrewqmnn
-1
Задание 2.
Верно.
Исследование.
Для проведения исследования был создан генератор случайных строк
определенной длины. Анализируя графики (см. рис. 1 и см. рис. 2) видно, что
время выполнения работы алгоритма растёт линейно. Стоит заметить, что
увеличение длины текста влияет на время меньше по сравнению с
увеличением длины подстроки. Это можно объяснить тем, что самая
затратная часть алгоритма – это вычисление префикс функции. Ее

5
вычисление зависит от длины подстроки, именно поэтому она тратит
большое количество времени.
Рисунок 1 – Фиксированная длина текста и изменяющаяся длина
подстроки
Рисунок 2 – Фиксированная длина подстроки и изменяющаяся длина
текста

6
Вывод.
Был изучен алгоритм Кнута-Мориса-Пратта для поиска подстроки в
строке.
Для этого в ходе выполнения лабораторной работы были написаны
программы для определения того, является ли одна строка циклическим
сдвигом другой строки, и для поиска подстроки в строке. Также был создан
генератор тестов для тестирования и исследования написанных программ.
В ходе исследования выяснилось, что время выполнения работы
алгоритма растёт линейно. Также было замечено, что увеличение длины
текста влияет на время меньше по сравнению с увеличением длины
подстроки. В добавок выяснилось, что вычисление префикс функции – это
самая затратная часть алгоритма.
Таким образом алгоритм Кнута-Мориса-Пратта для поиска подстроки в
строке может быть максимально эффективно использован при относительно
небольших размерах подстроки, однако даже при больших размерах
подстроки позволяет выполнять поиск за линейное время и превосходить по
скорости наивный поиск.

7
ПРИЛОЖЕНИЕ А
ИСХОДНЫЙ КОД ПРОГРАММЫ
Название файла: kmp.py
def prefix_function(pattern):
pattern_size, k = len(pattern), 0
prefix_values = [0] * pattern_size
for i in range(1, pattern_size):
while k > 0 and pattern[k] != pattern[i]:
k = prefix_values[k - 1]
if pattern[k] == pattern[i]:
k += 1
prefix_values[i] = k
return prefix_values
def kmp_search():
pattern_size, k = len(pattern), 0
prefix_values = prefix_function(pattern)
result = []
for i in range(len(text)):
while k > 0 and pattern[k] != text[i]:
k = prefix_values[k - 1]
if pattern[k] == text[i]:
k += 1
if k == pattern_size:
result.append(i - pattern_size + 1)
k = prefix_values[k - 1]
return result if result else [-1]
if __name__ == '__main__':
pattern = input()
text = input()
indices = kmp_search()
print(','.join(map(str, indices)))
Название файла: cycle.py
from kmp import prefix_function
def kmp_search(text, pattern):
pattern_size, k = len(pattern), 0
prefix_values = prefix_function(pattern)
for i in range(len(text)):
while k > 0 and pattern[k] != text[i]:
k = prefix_values[k - 1]
if pattern[k] == text[i]:
k += 1
if k == pattern_size:

8
return i - pattern_size + 1
return -1
def is_cyclic_shift():
if len(first) != len(second):
return -1
double_first = first + first
return kmp_search(double_first, second)
if __name__ == '__main__':
first = input()
second = input()
print(is_cyclic_shift())
Соседние файлы в папке Лабы по ПиАА 2024
