- •Алгоритмы и алгоритмические языки
- •Лекция 1 Представление чисел в эвм Целые
- •Вещественные
- •Ошибки вычислений
- •Лекция 2 Алгоритмы. Сведение алгоритмов. Нижние и верхние оценки.
- •Сортировки Постановка задачи
- •Сортировка пузырьком.
- •Сортировка слиянием с рекурсией.
- •Сортировка слиянием без рекурсии.
- •Лекция 3 Алгоритмы. Сведение алгоритмов. Сортировки и связанные с ними задачи.
- •Доказательство корректности работы алгоритма.
- •Оценки времени работы алгоритма.
- •Некоторые задачи, сводящиеся к сортировке.
- •Лекция 4 Алгоритмы. Сведение алгоритмов. Сортировки и связанные с ними задачи.
- •HeapSort или сортировка с помощью пирамиды.
- •Алгоритмы сортировки за время o(n)
- •Сортировка подсчетом
- •Цифровая сортировка
- •Сортировка вычерпыванием
- •Лекция 5 Алгоритмы. Сведение алгоритмов.
- •Порядковые статистики.
- •Поиск порядковой статистики за время (n) в среднем
- •Поиск порядковой статистики за время (n) в худшем случае
- •Язык программирования c.
- •Переменные
- •Структуры данных.
- •Вектор.
- •Лекция 6
- •Стек. Реализация 1 (на основе массива).
- •Стек. Реализация 2 (на основе массива с использованием общей структуры).
- •Стек. Реализация 3 (на основе указателей).
- •Стек. Реализация 4 (на основе массива из двух указателей).
- •Стек. Реализация 5 (на основе указателя на указатель).
- •Очередь.
- •Стандартная ссылочная реализация списков
- •Ссылочная реализация списков с фиктивным элементом
- •Реализация l2-списка на основе двух стеков
- •Реализация l2-списка с обеспечением выделения/освобождения памяти
- •Лекция 7 Структуры данных. Графы.
- •Поиск пути в графе с наименьшим количеством промежуточных вершин
- •Представление графа в памяти эвм
- •Массив ребер
- •Матрица смежности
- •Матрица инцидентности
- •Списки смежных вершин
- •Реберный список с двойными связями (для плоской укладки планарных графов)
- •Лекция 8 Структуры данных. Графы.
- •Поиск кратчайшего пути в графе
- •Алгоритм Дейкстры
- •Конец вечного цикла
- •Алгоритм Дейкстры модифицированный
- •Конец вечного цикла
- •Лекция 9 Бинарные деревья поиска
- •Поиск элемента в дереве
- •Добавление элемента в дерево
- •Поиск минимального и максимального элемента в дереве
- •Удаление элемента из дерева
- •Поиск следующего/предыдущего элемента в дереве
- •Слияние двух деревьев
- •Разбиение дерева по разбивающему элементу
- •Сбалансированные и идеально сбалансированные бинарные деревья поиска
- •Операции с идеально сбалансированным деревом
- •Операции со сбалансированным деревом
- •Поиск элемента в дереве
- •Добавление элемента в дерево
- •Удаление элемента из дерева
- •Поиск минимального и максимального элемента в дереве
- •Поиск следующего/предыдущего элемента в дереве
- •Слияние двух деревьев
- •Разбиение дерева по разбивающему элементу
- •Лекция 10 Красно-черные деревья
- •Отступление на тему языка с. Поля структур.
- •Отступление на тему языка с. Бинарные операции.
- •Высота красно-черного дерева
- •Добавление элемента в красно-черное дерево
- •Однопроходное добавление элемента в красно-черное дерево
- •Удаление элемента из красно-черного дерева
- •Лекция 11
- •Высота b-дерева
- •Поиск вершины в b-дереве
- •Отступление на тему языка с. Быстрый поиск и сортировка в языке с
- •Добавление вершины в b-дерево
- •Удаление вершины из b-дерева
- •Лекция 12 Хеширование
- •Метод многих списков
- •Метод линейных проб
- •Метод цепочек
- •Лекция 14 Поиск строк
- •Отступление на тему языка с. Ввод-вывод строк из файла
- •Алгоритм поиска подстроки с использованием хеш-функции (Алгоритм Рабина-Карпа)
- •Конечные автоматы
- •Отступление на тему языка с. Работа со строками
- •Алгоритм поиска подстроки, основанный на конечных автоматах
- •Лекция 15 Алгоритм поиска подстроки Кнута-Морриса-Пратта (на основе префикс-функции)
- •Алгоритм поиска подстроки Бойера-Мура (на основе стоп-символов/безопасных суффиксов)
- •Эвристика стоп-символа
- •Эвристика безопасного суффикса
- •Форматы bmp и rle
- •Bmp без сжатия.
Конечные автоматы
Начнем с примера тривиального конечного автомата.
Пусть у нас имеется некоторая кучка камней. В каждый момент состояние кучки q отражается числом, равным количеству камней в кучке. В начальный момент в кучке находилось q0 камней. Последовательно подаются запросы на добавление или удаление камня из кучки. Нас интересует момент, когда в кучке камней не останется или, что, то же самое, когда кучка придет к состоянию q=0.
Запросы на добавление/удаление камней можно проинтерпретировать как последовательность элементов ai из алфавита , состоящего всего из двух чисел: 1 и –1.
При появлении элемента ai состояние кучки изменяется, причем новое состояние можно вычислить как функцию от исходного состояния qi-1 и пришедшего элемента ai : qi-1 = ( qi , ai)=qi + ai. Функцию нам будет удобнее задавать таблицей
состояние\входные символы |
-1 |
1 |
0 |
0 |
1 |
1 |
0 |
2 |
2 |
1 |
3 |
3 |
2 |
3 |
Заметим, что данная табличная функция описывает кучку, состоящую не более чем из трех камней. Попытки занесения лишних камней, также как и попытка изъятия камня из пустующей кучки, игнорируются.
Осталось добавить, что выделенное состояние q=0, с точки зрения конечных автоматов, называется принимающим. Пример можно считать завершенным.
Сведя вместе все выделенные понятия из данного примера, можно дать строгое определение конечного автомата.
Конечным автоматом называется объект, состоящий из пяти множеств:
Q – конечное множество состояний;
A Q – подмножество принимающих состояний;
q0 Q – начальное состояние;
– конечный входной алфавит;
: Q A Q – функция перехода.
Функция перехода, обычно, задается таблицей, поэтому считается, что вычисление одного значения функции требует времени O(1).
Отметим, что алгоритмы, основанные на конечных автоматах, принципиально не вкладывается в схему алгоритмов, основанных на сравнениях, т.к. значение табличной функции не может быть вычислено в рамках алгоритмов, основанных на сравнениях, за время O(1).
Отступление на тему языка с. Работа со строками
В языке С есть очень удобная библиотека для работы со строками. Большинство функций библиотеки, безусловно, следует выучить и активно ими пользоваться. Описания функций содержатся в файле string.h. Подробное описание функций следует прочитать в документации по языку С. Здесь мы кратко приведем описание нескольких функций, которые будем использовать в дальнейшем при объяснении алгоритмов, чтобы не вводить новых понятий.
int strlen(const char *);//длина строки
char * strcpy(char *, const char *);//копирование второй строки в первую
char * strcat(char *, const char *);//подклеивание второй строки к первой
char * strstr(char *, const char *);//поиск второй строки в первой
int strcmp(const char *, const char *);//лексикографич.сравнение строк
int strncmp(const char *, const char *,int n);//лекс.сравн. первых n байт строк
Алгоритм поиска подстроки, основанный на конечных автоматах
С этого момента мы будем говорить о строках в понимании языка С.
Итак, в строке S, strlen(S)==N, следует найти все вхождения подстроки W, strlen(W)==M, т.е. следует найти все такие 0 i N-M, что strncmp(S+i,W,M)==0.
Будем говорить, что строка b является префиксом строки a, если
strlen(b)<=strlen(a) && strncmp(b,a,strlen(b))==0.
Будем говорить, что строка b является суффиксом строки a, если
strlen(b)<=strlen(a) && strcmp(b,a+strlen(a)-strlen(b))==0.
Основная идея алгоритма следующая: будем последовательно добавлять к входной строке S по одному символу из входного потока данных. При этом, каждый раз будем вычислять значение функции h(S,W), равной максимальной длине l суффикса строки S, совпадающего с префиксом строки W длины l:
strncmp(S+strlen(S)-l,W,l)==0
Например, для S=(ababa), W=(abac): h(S,W)=3.
Если, при этом, выполнится условие
h(S,W)==strlen(W)
то это будет обозначать, что найдено вхождение W в строку S.
Допустим, что в некоторый момент мы знаем значение функции h(S,W). Пусть строка S2 получена с помощью добавления очередного символа a из входного потока данных в конец строки S.
Легко увидеть, что h(S2,W)<= h(S,W)+1 (иначе, мы сразу получим, что строка S имеет суффикс длины большей h(S,W), совпадающий с префиксом W), но зная значение h(S,W) мы сразу получаем значения h(S,W) последних символов S (это – первые h(S,W) символов строки W). Т.о. значение функции h(S2,W) может быть вычислено исходя из знания значения h(S,W) и a.
Итак, мы строим конечный автомат, в котором состояние системы задается величиной H= h(S,W). В качестве входного алфавита будут выступать символы, текста. Принимающим будет такое состояние H, когда H==strlen(W). Начальное состояние H0=0. О вычислении функции перехода поговорим позднее.
Итак, легко увидеть, что, если не задумываться о вычислении функции перехода, то основная часть алгоритма поиска выполняется за время T=(N), где N – длина входной последовательности текста.
Функцию перехода предлагается вычислять в лоб. Т.е. для случая, когда ищется строка W и когда алфавит состоит из 256 символов, строится таблица tab из 256 столбцов и strlen(W) строк. j-ый столбец будет соответствовать появлению символа с кодом j, а i-ая строка будет соответствовать состоянию автомата i. Для получения значения tab[i][j] следует рассмотреть строку, состоящую из i первых символов строка W с добавленным в конец символом с кодом j. Длина максимального суффикса полученной строки, совпадающего с префиксом W, будет искомым значением tab[i][j].
Для получения значения tab[i][j] нужно не более i раз сравнить подстроку W с подстрокой полученной строки. Итого, tab[i][j] вычисляется за время O(M2). Все значения tab[i][j] вычисляются за время O(256M3), где 256 – количество символов входного алфавита, M – длина искомого слова. Легко увидеть, что для данного алгоритма данная оценка точна. Т.о. мы доказали следующую теорему
Теорема. Поиск подстроки длины M, состоящей из символов алфавита из K символов, в тексте длины N с помощью предложенного алгоритма, использующего конечные автоматы, требует основного времени T1=(N). На подготовку, зависящую только от искомой подстроки и размера входного алфавита, требуется время T0=( K M3).