Алгоритм Кнута-Морриса-Пратта (КМП) — это эффективный алгоритм поиска подстроки в строке, разработанный для улучшения производительности по сравнению с наивными методами. Он работает за время O(n + m)
, где n
— длина строки, а m
— длина подстроки. Алгоритм использует предварительную обработку подстроки для создания префикс-функции, что позволяет избегать повторного сравнения символов.
Основная идея алгоритма
Алгоритм КМП использует префикс-функцию для подстроки. Префикс-функция определяет, на сколько символов можно сдвинуть подстроку, не теряя уже совпавшую часть. Это позволяет пропускать некоторые символы в строке, тем самым ускоряя процесс поиска.
Построение префикс-функции
Префикс-функция для строки p
длины m
— это массив pi
, где pi[i]
— это длина наибольшего собственного префикса подстроки p[0...i]
, который также является суффиксом этой подстроки.
Пример
Для строки p = "ababc"
префикс-функция будет [0, 0, 1, 2, 0]
.
Этапы работы алгоритма
- Построение префикс-функции для подстроки.
- Поиск подстроки в строке с использованием префикс-функции.
Построение префикс-функции
#include <iostream>
#include <vector>
#include <string>
// Функция для вычисления префикс-функции
std::vector<int> computePrefixFunction(const std::string &pattern) {
int m = pattern.length();
std::vector<int> pi(m, 0);
int k = 0;
for (int i = 1; i < m; i++) {
while (k > 0 && pattern[i] != pattern[k]) {
k = pi[k - 1];
}
if (pattern[i] == pattern[k]) {
k++;
}
pi[i] = k;
}
return pi;
}
Поиск подстроки
#include <iostream>
#include <vector>
#include <string>
// Функция для поиска подстроки в строке
std::vector<int> KMPSearch(const std::string &text, const std::string &pattern) {
int n = text.length();
int m = pattern.length();
std::vector<int> pi = computePrefixFunction(pattern);
std::vector<int> result;
int k = 0;
for (int i = 0; i < n; i++) {
while (k > 0 && text[i] != pattern[k]) {
k = pi[k - 1];
}
if (text[i] == pattern[k]) {
k++;
}
if (k == m) {
result.push_back(i - m + 1);
k = pi[k - 1];
}
}
return result;
}
int main() {
std::string text = "ababcababcab";
std::string pattern = "ababc";
std::vector<int> result = KMPSearch(text, pattern);
for (int pos : result) {
std::cout << "Pattern found at index " << pos << std::endl;
}
return 0;
}
Объяснение
-
Построение префикс-функции:
- Для каждой позиции
i
в подстрокеpattern
мы вычисляем длину наибольшего префикса, который также является суффиксом подстрокиpattern[0...i]
. - Значение
pi[i]
используется для определения, сколько символов можно пропустить в случае несовпадения.
- Для каждой позиции
-
Поиск подстроки:
- Проходим по строке
text
и подстрокеpattern
. - Для каждой позиции
i
в строке, если символыtext[i]
иpattern[k]
совпадают, увеличиваемk
. - Если символы не совпадают, используем значение
pi[k-1]
для определения новой позицииk
. - Если достигли конца подстроки (т.е.
k == m
), это означает, что подстрока найдена в строке, и мы сохраняем позицию вхождения.
- Проходим по строке
Преимущества алгоритма
- Эффективность: Алгоритм работает за время
O(n + m)
, что значительно быстрее по сравнению с наивным методомO(n * m)
. - Простота реализации: Несмотря на свою эффективность, алгоритм довольно прост для понимания и реализации.
- Универсальность: Алгоритм подходит для решения различных задач, связанных с поиском подстрок, например, в текстовых редакторах, биоинформатике и других областях.
Алгоритм Кнута-Морриса-Пратта — это мощный инструмент для поиска подстрок, который позволяет эффективно решать задачи обработки строк.