Лабы по ПиАА 2024 / Baraeva_Elizaveta_lb4
.docМИНОБРНАУКИ РОССИИ
Санкт-Петербургский государственный
электротехнический университет
«ЛЭТИ» им. В. И. Ульянова (Ленина)
Кафедра МО ЭВМ
отчет
по лабораторной работе №4
по дисциплине «Построение и анализ алгоритмов»
Тема: Алгоритм Кнута-Морриса-Пратта
Студентка гр. 2383 |
|
Бараева Е. Н. |
Преподаватель |
|
Шевелева А. М. |
Санкт-Петербург
2024
Цель работы.
Изучить алгоритм Кнут-Моррис-Пратта для поиска подстроки в строке, и применить его для решения задачи об определении того, что является ли одна строка циклическим сдвигом другой.
Задание.
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:
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 с помощью префикс-функции для последнего символа подстроки. В конце возвращается список 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) видно, что время выполнения работы алгоритма растёт линейно. Стоит заметить, что увеличение длины текста влияет на время меньше по сравнению с увеличением длины подстроки. Это можно объяснить тем, что самая затратная часть алгоритма – это вычисление префикс функции. Ее вычисление зависит от длины подстроки, именно поэтому она тратит большое количество времени.
Рисунок 1 – Фиксированная длина текста и изменяющаяся длина подстроки
Рисунок 2 – Фиксированная длина подстроки и изменяющаяся длина текста
Вывод.
Был изучен алгоритм Кнута-Мориса-Пратта для поиска подстроки в строке.
Для этого в ходе выполнения лабораторной работы были написаны программы для определения того, является ли одна строка циклическим сдвигом другой строки, и для поиска подстроки в строке. Также был создан генератор тестов для тестирования и исследования написанных программ.
В ходе исследования выяснилось, что время выполнения работы алгоритма растёт линейно. Также было замечено, что увеличение длины текста влияет на время меньше по сравнению с увеличением длины подстроки. В добавок выяснилось, что вычисление префикс функции – это самая затратная часть алгоритма.
Таким образом алгоритм Кнута-Мориса-Пратта для поиска подстроки в строке может быть максимально эффективно использован при относительно небольших размерах подстроки, однако даже при больших размерах подстроки позволяет выполнять поиск за линейное время и превосходить по скорости наивный поиск.
ПРИЛОЖЕНИЕ А ИСХОДНЫЙ КОД ПРОГРАММЫ
Название файла: 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:
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())
