Суффиксный автомат — различия между версиями
|  (→Реализация) | |||
| Строка 82: | Строка 82: | ||
|       p = last |       p = last | ||
| − |       '''while''' p >= 0 '''and''' edges[p].find(c) == ''null | + |       '''while''' p >= 0 '''and''' edges[p].find(c) == ''null'' | 
|           edges[p][c] = cur |           edges[p][c] = cur | ||
|           p = link[p] |           p = link[p] | ||
| − |       '''if''' p != -1 | + |       '''if''' p != -1 | 
|           q = edges[p][c] |           q = edges[p][c] | ||
| − |           '''if''' len[p] + 1 == len[q] | + |           '''if''' len[p] + 1 == len[q] | 
|               link[cur] = q |               link[cur] = q | ||
| − |           '''else | + |           '''else''' | 
|               new = clone(q)                                <font color="green">// скопируем состояние <tex>q</tex></font> |               new = clone(q)                                <font color="green">// скопируем состояние <tex>q</tex></font> | ||
|               len[new] = len[p] + 1 |               len[new] = len[p] + 1 | ||
|               link[q] = link[cur] = new |               link[q] = link[cur] = new | ||
| − |               '''while''' p >= 0 '''and''' edges[p][c] == q | + |               '''while''' p >= 0 '''and''' edges[p][c] == q | 
|                   edges[p][c] = new |                   edges[p][c] = new | ||
|                   p = link[p] |                   p = link[p] | ||
Версия 19:25, 26 марта 2016
| Определение: | 
| Суффиксный автомат (англ. suffix automaton, directed acyclic word graph) — минимальный ДКА, который принимает все суффиксы строки и только их. | 
Содержание
Описание
Детерминированным конечным автоматом называется пятёрка (), где
- — множество состояний,
- — начальное состояние,
- — конечный алфавит,
- — функция переходов,
- — множество терминальных состояний.
Суффиксный автомат для строки представляет собой ациклический ориентированный граф, с начальной вершиной и множеством терминальных вершин, рёбра которого помечены символами .
| Определение: | 
| Состояние принимает строку , если существует путь из начального состояния в , такой, что если последовательно выписать буквы на рёбрах на пути, получим строку . | 
| Определение: | 
| Автомат принимает строку , если её принимает хотя бы одно из финальных состояний. | 
Так как автомат детерминированный, то каждому пути соответствует строка. 
Если две строки и принимаются одним состоянием произвольного автомата, то для любой строки строки и принимаются или не принимаются автоматом одновременно. Действительно, независимо от того, как мы пришли в состояние , если мы пройдём из него по пути, соответствующему строке , мы сможем точно сказать, в какое состояние мы попадём (в частности, будет ли оно финальным). Значит, любому состоянию соответствует множество строк , которые переводят его в одно из конечных состояний.
| Определение: | 
| Множество называют правым контекстом состояния. | 
Правый контекст определен не только для состояния, но и для строк, которые оно принимает — правый контекст строк совпадает с правым контекстом состояния.
| Утверждение: | 
| Состояний в автомате не меньше, чем различных правых контекстов у подстрок строки, для которой он построен, причём в минимальном автомате достигается нижняя оценка. | 
| Допустим, что в автомате есть два состояния и такие что . Мы можем удалить состояние и перевести переходы, ведущие в него в состояние . Множество принимаемых строк от этого не изменится, следовательно, мы можем продолжать, пока количество состояний не будет равно числу попарно различных правых контекстов. | 
Таким образом, ДКА является минимальным тогда и только тогда, когда правые контексты всех его состояний попарно различны. В случае суффиксного автомата правый контекст строки взаимнооднозначно соответствует множеству правых позиций вхождений строки в строку . Таким образом, каждое состояние автомата принимает строки с одинаковым множеством правых позиций их вхождений и обратно, все строки с таким множеством позиций принимается этим состоянием.
Построение
Алгоритм
| Определение: | 
| Пусть длина самой короткой строки, которая принимается состоянием равно , тогда суффиксная ссылка будет вести из этого состояния в состояние, которое принимает эту же строку без первого символа. | 
Будем обозначать длину самой длинной строки, которая принимается состоянием  как . Длина самой короткой строки из  в таком случае будет равна . 
Суффиксный автомат может быть построен за линейное время (при константном размере алфавита) online-алгоритмом. Будем добавлять символы строки  по одному, перестраивая при этом автомат. 
Изначально автомат состоит из одного состояния, для которого , а .
Обозначим состояние , соответствующее текущей строке до добавления символа  (изначально ). 
Создадим новое состояние , . 
Рассмотрим все переходы из  по текущему символу . Если перехода нет, то добавляем переход в , переходим по суффиксной ссылке и повторяем процедуру снова. Если переход существует, то остановимся и обозначим текущее состояние за . Если перехода не нашлось и по суффиксным ссылкам мы дошли до фиктивного состояния (на которое указывает ), то .
Допустим, что мы остановились в состоянии , из которого существует переход с символом . Обозначим состояние, куда ведёт переход, через . Рассмотрим два случая:
-  Если , то .
- В противном случае, создадим новое состояние , скопируем в него вместе с суффиксными ссылками и переходами. присвоим значение . Перенаправим суффиксную ссылку из в и добавим ссылку из в . Пройдём по всем суффиксным ссылкам из состояния и все переходы в состояние по символу перенаправим в .
Обновим значение .
Пример построения
Рассмотрим построение суффиксного автомата для строки . Серыми стрелками показаны суффиксные ссылки.
-  Изначально автомат состоит из одного начального состояния. .
-  Добавляем символ . Создаем состояние . Переходов из начального состояния по символу  нет, перейти по суффиксным ссылкам некуда, значит добавим суффиксную ссылку . .
-  Добавляем символ . Создаем состояние . Добавим переход из , откатимся по суффиксной ссылке и добавим переход из . Добавим суффиксную ссылку . .
-  Аналогично добавим символ  и обновим автомат. .
-  Добавляем символ . Добавим переход из  и перейдем по суффиксной ссылке в начальное состояние. Из состояния  существует переход по символу .
-  Рассмотрим состояние , куда существует переход. Имеем .
- Создаем новое состояние .
- Копируем в него все суффиксные ссылки и переходы из и присвоим .
-  Перенаправим суффиксную ссылку из  в  и добавим ссылку из  в . Перенаправим переход  в состояние .
 
-  Построение автомата завершено. Чтобы пометить терминальные вершины, найдём состояние, которое принимает строку  и пройдём по суффиксным ссылкам, помечая все посещенные состояния терминальными.
Реализация
- Переходы хранятся в массиве отображений (ключ — символ, значение — номер состояния) ,
- Суффиксные ссылки хранятся в массиве ,
- Длины строк хранятся в массиве ,
- Функция создаёт новое состояние и возвращает его номер,
- Функция копирует состояние и возвращает номер нового состояния.
func addChar(c):
    cur = newState()                                       // создаём новое состояние и возвращаем его номер 
    p = last
    while p >= 0 and edges[p].find(c) == null
        edges[p][c] = cur
        p = link[p]
    if p != -1
        q = edges[p][c]
        if len[p] + 1 == len[q]
            link[cur] = q
        else
            new = clone(q)                                // скопируем состояние 
            len[new] = len[p] + 1
            link[q] = link[cur] = new
            while p >= 0 and edges[p][c] == q
                edges[p][c] = new
                p = link[p]
    last = cur
Применение
Проверка вхождения строки
| Задача: | 
| Даны строки и . Требуется проверить, является ли строка подстрокой . | 
Построим суффиксный автомат для строки .
Пусть текущее состояние — , изначально равно  (начальному состоянию).
Будем по очереди обрабатывать символы строки . Если из состояния  есть переход в по текущему символу, но перейдем в новое состояние и повторим процедуру. Если перехода не существует, то  не является подстрокой . Если успешно обработали все символы , то она является подстрокой .
Асимптотика — построение суфавтомата за , проверка за .
Позиция первого вхождения строки
| Задача: | 
| Даны строки и . Требуется найти позицию первого вхождения строки в . | 
Построим суффиксный автомат для строки .
В процессе построения для каждого состояния  будем хранить значение  — позицию окончания первого вхождения строки.
Поддерживать позицию  можно следующим образом: при добавлении нового состояния , а при клонировании вершины .
Для поиска вхождения обойдём автомат, как в предыдущей задаче. Пусть состояние  в автомате соответствует строке . Тогда ответ на задачу .
Асимптотика — построение суфавтомата за , проверка за .
Количество различных подстрок
| Задача: | 
| Дана строка . Найти количество различных подстрок строки . | 
Построим суффиксный автомат для строки .
Каждой подстроке в суффиксном автомате соответствует путь, тогда ответ на задачу — количество различных путей из начальной вершины. Так как суфавтомат представляет собой ациклический граф, мы можем найти количество путей в графе методом динамического программирования.
Сравнение с другими суффиксными структурами
Пусть — строка, для которой строим соответствующую структуру, , — алфавит.
| Время работы | Память | |
| Суффиксный бор | ||
| Сжатое суффиксное дерево | ||
| Суффиксный массив | ||
| Суффиксный автомат | 
Источники информации
- Maxime Crochemore, Christophe Hancart, Thierry Lecroq — Algorithms on Strings
- А. Кульков — Лекция по суффиксным структурам
- MAXimal :: algo :: Суффиксный автомат








