Приложение. Листинг программных модулей
#include <iostream>
#include <vector>
#include <string>
#include <chrono>
#include <cstdlib>
#include <ctime>
#include <iomanip>
using namespace std;
using namespace chrono;
// Алфавит из 6 символов (a-f)
const string ALPHABET = "abcdef";
// Фиксированный паттерн
const string PATTERN = "abbfedfa";
const int PAT_LEN = PATTERN.size();
// Генерация случайного символа из алфавита
char getRandomChar() {
return ALPHABET[rand() % ALPHABET.size()];
}
// Генерация случайной строки длины len
string random_string(int len) {
string s(len, ' ');
for (int i = 0; i < len; ++i)
s[i] = getRandomChar();
return s;
}
// Генерация текста
string generate_text(int L) {
string text = random_string(L);
int start = rand() % (L - PAT_LEN + 1);
int pos = start;
while (pos <= L - PAT_LEN) {
for (int i = 0; i < PAT_LEN; ++i)
text[pos + i] = PATTERN[i];
int period = 10 + rand() % 11; // 10..20
pos += period;
}
return text;
}
// Наивный поиск
int find_substrings(const string& S, const string& W) {
unsigned int i, j;
int number = 0;
for (i = 0; i < S.length() - W.length() + 1; i++) {
j = 0;
while ((j < W.length()) && (W[j] == S[i + j])) {
j = j + 1;
}
if (j == W.length()) {
number++;
}
}
return number;
}
// КМП
int KMPSearch(const string& text, const string& pattern) {
int n = text.length();
int m = pattern.length();
int count = 0;
// Создаем префикс-функцию
vector<int> pi(m);
pi[0] = 0;
int k = 0;
for (int i = 1; i < m; i++) {
while (k > 0 && pattern[k] != pattern[i])
k = pi[k - 1];
if (pattern[k] == pattern[i])
k++;
pi[i] = k;
}
// Поиск подстроки
k = 0;
for (int i = 0; i < n; i++) {
while (k > 0 && pattern[k] != text[i])
k = pi[k - 1];
if (pattern[k] == text[i])
k++;
if (k == m) {
count++;
k = pi[k - 1]; // продолжаем поиск
}
}
return count;
}
int main() {
srand(static_cast<unsigned>(time(nullptr)));
// Параметры эксперимента
const int MIN_LEN = 100;
const int MAX_LEN = 2000;
const int STEP = 100;
const int NUM_TEXTS = 20; // число разных текстов для каждой длины
const int REPEATS_PER_TEXT = 500; // число повторений на каждом тексте
cout << fixed << setprecision(3);
cout << "Length\tNaive (micros)\tKMP (micros)\n";
for (int L = MIN_LEN; L <= MAX_LEN; L += STEP) {
double naive_total_time = 0;
double kmp_total_time = 0;
long long naive_total_count = 0;
long long kmp_total_count = 0;
for (int t = 0; t < NUM_TEXTS; ++t) {
// Генерируем новый текст
string text = generate_text(L);
// Измеряем overhead пустого цикла
auto start = high_resolution_clock::now();
for (int r = 0; r < REPEATS_PER_TEXT; ++r) {
volatile int dummy = 0;
(void)dummy;
}
auto end = high_resolution_clock::now();
double loop_overhead = duration<double, micro>(end - start).count();
// Наивный поиск
start = high_resolution_clock::now();
for (int r = 0; r < REPEATS_PER_TEXT; ++r) {
naive_total_count += find_substrings(text, PATTERN);
}
end = high_resolution_clock::now();
naive_total_time += (duration<double, micro>(end - start).count() - loop_overhead);
// КМП поиск
start = high_resolution_clock::now();
for (int r = 0; r < REPEATS_PER_TEXT; ++r) {
kmp_total_count += KMPSearch(text, PATTERN);
}
end = high_resolution_clock::now();
kmp_total_time += (duration<double, micro>(end - start).count() - loop_overhead);
}
// Проверка корректности
if (naive_total_count != kmp_total_count) {
cerr << "Mismatch at L=" << L << ": naive=" << naive_total_count
<< ", kmp=" << kmp_total_count << endl;
}
// Усредняем
double naive_avg = naive_total_time / (NUM_TEXTS * REPEATS_PER_TEXT);
double kmp_avg = kmp_total_time / (NUM_TEXTS * REPEATS_PER_TEXT);
// Вывод
cout << L << "\t" << naive_avg << "\t\t" << kmp_avg << "\n";
}
return 0;
}
