Скачиваний:
1
Добавлен:
22.09.2026
Размер:
71 Кб
Скачать
☆

ФЕДЕРАЛЬНОЕ АГЕНТСТВО СВЯЗИ

ФЕДЕРАЛЬНОЕ ГОСУДАРСТВЕННОЕ БЮДЖЕТНОЕ ОБРАЗОВАТЕЛЬНОЕ УЧРЕЖДЕНИЕ ВЫСШЕГО ОБРАЗОВАНИЯ

«САНКТ-ПЕТЕРБУРГСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ ТЕЛЕКОММУНИКАЦИЙ ИМ. ПРОФ. М.А. БОНЧ-БРУЕВИЧА»

(СПбГУТ)

Дисциплина: «Алгоритмы и структуры данных»

Лабораторная работа №2.

Выполнил: Студент группы ИКПИ-41

Летягина Е. А.

Проверил: Кандидат технических наук, доцент кафедры ПИиВТ

Дагаев А. В.

г. Санкт-Петербург

2026

  1. Описание алгоритмов

Для сравнения были выбраны два алгоритма поиска подстроки в строке: наивный (прямой) поиск и алгоритм Кнута‑Морриса‑Пратта (КМП).

Наивный поиск

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

Временная сложность:

  • В лучшем случае O(n), если подстрока встречается в самом начале;

  • В среднем и худшем случаях O(nm)

Алгоритм Кнута‑Морриса‑Пратта

Алгоритм КМП использует предварительную обработку подстроки – вычисление префикс-функции. Префикс-функция для каждого индекса i показывает длину наибольшего собственного префикса подстроки, который одновременно является суффиксом для подстроки [0..i]. Благодаря этой информации при несовпадении можно сдвинуть подстроку не на один символ, а на величину, позволяющую пропустить заведомо неподходящие позиции.

Временная сложность:

  • Построение префикс-функции: O(m);

  • Поиск: O(n);

  • Общая сложность: O(n+m) во всех случаях.

  1. Описание программы

Программа предназначена для сравнения времени выполнения двух алгоритмов поиска подстроки: наивного и КМП. Измерение производится многократным запуском алгоритмов на текстах разной длины с последующим усреднением времени для повышения точности.

Длина текста изменяется от 100 до 2000 символов с шагом 100. Фиксированная подстрока: " abbfedfa" (длина 8 символов) вставляется в текст со случайным периодом от 10 до 20 символов, начиная со случайной позиции. Для каждой длины текста генерируется 20 различных текстов, на каждом из которых каждый алгоритм выполняется по 500 раз. Результаты усредняются.

Функции программы

  • char getRandomChar() – возвращает случайный символ из алфавита "abcdef".

  • string random_string(int len) – генерирует случайную строку заданной длины.

  • string generate_text(int L) – создаёт текст длины L с вставками фиксированного паттерна.

  • int find_substrings(const string& S, const string& W) – реализует наивный поиск, возвращает количество вхождений.

  • int KMPSearch(const string& text, const string& pattern) – реализует алгоритм КМП, возвращает количество вхождений. Внутри вычисляет префикс-функцию и выполняет поиск.

  • main() – управляет экспериментом, выводит таблицу результатов.

  1. Сравнительный анализ алгоритмов

  1. Рассмотрим работу наивного (прямого) алгоритма поиска подстроки на примере строки "xabbfedfyabbfedfz" (длина 17 символов) и подстроки "abbfedf" (длина 7 символов). Подстрока встречается в тексте дважды: на позициях 1 и 9 (нумерация с 0).

Процесс поиска заключается в последовательном сравнении подстроки с фрагментами текста, начиная с каждой возможной позиции. Если фрагмент полностью совпадает с подстрокой, фиксируется вхождение, и алгоритм продолжает со следующей позиции.

Рассмотрим работу алгоритма в таблице 1.

Шаг

Позиция

Сравниваемый фрагмент

Результат сравнения

Действие

1

0

x a b b f e d

Первый символ x ≠ a – несовпадение

Сдвиг +1

2

1

a b b f e d f

Все символы совпадают → первое вхождение

Сдвиг +1

3

2

b b f e d f y

Первый символ b ≠ a – несовпадение

Сдвиг +1

4

3

b f e d f y a

Первый символ b ≠ a – несовпадение

Сдвиг +1

5

4

f e d f y a b

Первый символ f ≠ a – несовпадение

Сдвиг +1

6

5

e d f y a b b

Первый символ e ≠ a – несовпадение

Сдвиг +1

7

6

d f y a b b f

Первый символ d ≠ a – несовпадение

Сдвиг +1

8

7

f y a b b f e

Первый символ f ≠ a – несовпадение

Сдвиг +1

9

8

y a b b f e d

Первый символ y ≠ a – несовпадение

Сдвиг +1

10

9

a b b f e d f

Все символы совпадают → второе вхождение

Сдвиг +1

11

10

b b f e d f z

Первый символ b ≠ a – несовпадение (выход за границы, поиск завершён)

-

Таблица 1 – Работа наивного алгоритма

2. Рассмотрим работу алгоритма КМП на примере поиска подстроки «abcabc» (длина m = 6). Текст «ababcabcabc» (длина n = 11).

При вычислении префикс-функции для подстроки мы последовательно рассматриваем каждый её символ, начиная с первого. Для позиции i (индексация с 0) мы ищем наибольшую длину такого собственного префикса подстроки, который одновременно является суффиксом для части подстроки, заканчивающейся в позиции i.

На примере подстроки «abcabc» (длина 6) процесс выглядит так:

  1. Для i = 0 (символ 'a') значение π[0] всегда равно 0.

  2. Для i = 1 (символ 'b'): возможные префиксы: «a»; суффиксы: «b». Совпадения нет → π[1] = 0.

  3. Для i = 2 (символ 'c'): префиксы: «a», «ab»; суффиксы: «c», «bc». Совпадений нет → π[2] = 0.

  4. Для i = 3 (символ 'a'): префиксы: «a», «ab», «abc»; суффиксы: «a», «ca», «bca». Совпадает только префикс «a» и суффикс «a» длиной 1 → π[3] = 1.

  5. Для i = 4 (символ 'b'): префиксы: «a», «ab», «abc», «abca»; суффиксы: «b», «ab», «cab», «bcab». Совпадает префикс «ab» и суффикс «ab» длиной 2 → π[4] = 2.

  6. Для i = 5 (символ 'c'): префиксы: «a», «ab», «abc», «abca», «abcab»; суффиксы: «c», «bc», «abc», «cabc», «bcabc». Совпадает префикс «abc» и суффикс «abc» длиной 3 → π[5] = 3.

Таким образом, получаем массив значений префикс-функции π = [0, 0, 0, 1, 2, 3]. Для более удобной визуализации поиск префикс-функции представлен в таблице 2.

Позиция

Символ

Подстрока

Префиксы

Суффиксы

π[i]

0

a

a

-

-

0

1

b

ab

{a}

{b}

0

2

c

abc

{a, ab}

{c, bc}

0

3

a

abca

{a, ab, abc}

{a,ca,bca}

1

4

b

abcab

{a, ab, abc, abca}

{b, ca,bca,bcab}

2

5

c

abcabc

{ a, ab, abc, abca, abcab}

{ b, ca, bca, bcab, bcabc}

3

Таблица 2 – Поиск префикс-функции

Поиск паттерна в тексте представлен в таблице 3. W – подстрока, S – текст, k — Счётчик длины текущего совпадения.

Позиция

Символ

k(до)

Действие

k(после)

Комментарий

0

a

0

W[0]==S[0] → k++

1

Начало совпадения

1

b

1

W[1]==S[1] → k++

2

Продолжение

2

a

2

W[2]='c' ≠ S[2]='a' → k=π[1]=0

0

Несовпадение, откат по π

2

a

0

W[0]==S[2] → k++

1

Новое начало с позиции 2

3

b

1

W[1]==S[3] → k++

2

-

4

c

2

W[2]==S[4] → k++

3

-

5

a

3

W[3]==S[5] → k++

4

-

6

b

4

W[4]==S[6] → k++

5

-

7

c

5

W[5]==S[7] → k==6

3

Вхождение найдено

8

a

3

W[3]==S[8] → k++

4

Продолжаем с учётом перекрытия

9

b

4

W[4]==S[9] → k++

5

-

10

c

5

W[5]==S[10] → k==6

3

Вхождение найдено

Таблица 3 – Работа алгоритма КМП

Текст: a b a b c a b c a b c

Паттерн: a b c a b c → вхождение #1 (начало с позиции 2)

Паттерн: a b c a b c → вхождение #2 (начало с позиции 5)

Сравнение алгоритмов

Для сравнения алгоритмов поиска подстроки методами наивного поиска и КМП был проведён ряд прогонов программы для различного количества символов в строке. На рисунке 1 представлена зависимость времени выполнения алгоритмов от количества элементов в массиве.

Рисунок 1 – График зависимости времени выполнения алгоритмов от длины строки

Из графика (рисунок 1) видно, что алгоритм Кнута-Морриса-Пратта работает быстрее наивного поиска, особенно при увеличении длины текста. Наивный поиск демонстрирует более резкий рост времени выполнения, что объясняется его сложностью O(n·m), где n – длина текста, m – длина подстроки (в данном случае 8 символов). Алгоритм КМП благодаря предварительному вычислению префикс-функции имеет линейную сложность O(n+m) и на практике показывает меньшее время.Рассмотрим каждый алгоритм подробнее.

Алгоритм наивного поиска имеет квадратичную сложность в худшем случае (O(n·m)). Как видно из графика 1, на небольших текстах (до 500 символов) он работает достаточно быстро, но с ростом длины текста время выполнения увеличивается заметно быстрее, чем у КМП. Это ограничивает его применение только случаями с короткими текстами или когда подстрока встречается в самом начале.

Рассмотрим график алгоритма КМП (рисунок 1). Он демонстрирует линейный рост времени, пропорциональный O(n), что подтверждается пологим характером кривой. Даже при максимальной длине текста (2000 символов) время остаётся небольшим, а рост замедляется благодаря отсутствию квадратичной зависимости. Алгоритм стабильно показывает высокую производительность на всех тестируемых объёмах данных, что делает его предпочтительным выбором для задач, требующих гарантированно быстрого поиска.

После анализа работы алгоритмов можно сделать следующие выводы:

Наивный поиск имеет теоретическую и практическую сложность O(n·m) в среднем и худшем случаях. Это подтверждается экспериментальными данными: при увеличении длины текста время выполнения растёт линейно, но с большим коэффициентом, чем у КМП. Алгоритм прост в реализации и эффективен только для очень коротких текстов или когда подстрока мала.

Алгоритм КМП гарантирует сложность O(n+m) во всех случаях, что делает его значительно более производительным на больших объёмах данных. Эксперимент подтверждает теоретические ожидания: даже при максимальной длине текста время остаётся небольшим, а рост замедляется благодаря оптимальному использованию префикс-функции. Это позволяет рекомендовать алгоритм КМП для обработки длинных текстов, где важна предсказуемая производительность.

Выводы

В ходе выполнения лабораторной работы были достигнуты следующие результаты:

  1. Изучены два алгоритма поиска подстроки: наивный (прямой) поиск и алгоритм Кнута‑Морриса‑Пратта (КМП). Рассмотрены их теоретические характеристики, включая временную сложность в лучшем, среднем и худшем случаях. Для наивного поиска сложность составляет O(n·m) (где n – длина текста, m – длина подстроки), для КМП – O(n+m) благодаря предварительному вычислению префикс-функции.

  2. Разработана программа на языке C++, реализующая оба алгоритма и позволяющая измерять время их выполнения. Программа выполняет многократные запуски алгоритмов на различных текстах для усреднения результатов и учитывает накладные расходы (пустой цикл), что обеспечивает достоверность полученных значений.

  3. Экспериментально получены зависимости времени выполнения от длины текста (от 100 до 2000 символов с шагом 100), построены соответствующие графики. Анализ графиков подтвердил теоретические оценки сложности: наивный поиск демонстрирует линейный рост с большим коэффициентом, что соответствует O(n·m) при фиксированном m, тогда как алгоритм КМП сохраняет более низкое время благодаря линейной сложности O(n+m). На практике КМП оказался быстрее наивного поиска.

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

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

Соседние файлы в папке Отчёты Дагаев