{"id":18479511,"url":"https://github.com/lonelywh1te/ukkonen-algorithm","last_synced_at":"2025-05-13T18:45:32.292Z","repository":{"id":65504884,"uuid":"569998723","full_name":"lonelywh1te/ukkonen-algorithm","owner":"lonelywh1te","description":"Implementation of the Ukkonen algorithm","archived":false,"fork":false,"pushed_at":"2023-01-27T13:55:47.000Z","size":9318,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-02-16T20:47:35.393Z","etag":null,"topics":["algorithms-and-data-structures","binary-tree"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lonelywh1te.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-11-24T05:18:15.000Z","updated_at":"2024-03-01T22:45:07.000Z","dependencies_parsed_at":"2023-02-14T17:15:26.667Z","dependency_job_id":null,"html_url":"https://github.com/lonelywh1te/ukkonen-algorithm","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lonelywh1te%2Fukkonen-algorithm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lonelywh1te%2Fukkonen-algorithm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lonelywh1te%2Fukkonen-algorithm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lonelywh1te%2Fukkonen-algorithm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lonelywh1te","download_url":"https://codeload.github.com/lonelywh1te/ukkonen-algorithm/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254007682,"owners_count":21998661,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["algorithms-and-data-structures","binary-tree"],"created_at":"2024-11-06T12:15:14.333Z","updated_at":"2025-05-13T18:45:32.253Z","avatar_url":"https://github.com/lonelywh1te.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Алгоритм Укконена\n_Берегалов А.С  \nДальневосточный федеральный университет  \nБ9121.09.03.03пикд  \n2022_\n\n## Содержание\n- [Содержание]\n- [Глоссарий]\n- [Введение]\n- [Определение]\n- [Построение дерева за линейное время]\n    - [Строка без повторений]\n    - [Строка с повторениями]\n- [Теория]\n    - [Правила продления суффиксов]\n    - [Наивный метод]\n    - [Суффиксные ссылки]\n        - [Построение суффиксных ссылок]\n        - [Использование суффиксных ссылок]\n        - [Лемма-1]\n        - [Лемма-2]\n        - [Асимптотика алгоритма с использованием суффиксных ссылок]\n    - [Алгоритм O(n)]\n        - [Итоговая оценка времени работы]\n- [Тестирование]\n    - [Входные данные]\n    - [Выходные данные]\n- [Анализ производительности]\n    - [Функция построения дерева]\n    - [Функция поиска подстроки]\n## Глоссарий\n- Бор (англ. trie, луч, нагруженное дерево) — структура данных для хранения набора строк, представляющая из себя подвешенное дерево с символами на рёбрах.\n- Внутренняя вершина — любая вершина дерева, имеющая потомков, и таким образом, не являющаяся листом.\n- Корень — самый верхний узел дерева.\n- Лист — узел, не имеющий дочерних элементов (детей).\n- Неявное суффиксное дерево (англ. implicit suffix tree, IST) строки S — это суффиксное дерево, построенное для строки S без добавления $.\n- Подстрока — это часть строки, состоящая из некоторого количества смежных символов исходной строки.\n- Суффикс — это подстрока строки S, начинающеяся в позиции i и зканчивающеяся в m - где m это длина строки S.\n## Введение\nАлгоритм Укконена основывается на понятии суффиксного дерева\n\n\u003e _Суффиксное дерево — бор, содержащий все суффиксы некоторой строки (и только их).  \nВходные данные для алгоритма – это строка s, состоящая из n символов s[0], s[1], …, s[n-1]._ [[17]](https://ru.wikipedia.org/wiki/%D0%A1%D1%83%D1%84%D1%84%D0%B8%D0%BA%D1%81%D0%BD%D0%BE%D0%B5_%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE)\n\nОно выглядит следующим образом:  \n\n![Дерево для строки \"abcxa\"](/images/img0.jpg)  \n_Дерево для строки \"abcxa\"_\n\nСуффиксное дерево — дерево с n листьями, обладающее следующими свойствами:\n - каждая внутренняя вершина дерева имеет не меньше двух детей;\n - каждое ребро помечено непустой подстрокой строки s;\n - никакие два ребра, выходящие из одной вершины, не могут иметь пометок, начинающихся с одного и того же символа;\n - дерево должно содержать все суффиксы строки s, причем каждый суффикс заканчивается точно в листе и нигде кроме него.\n\nЧисло вершин в таком дереве — O(n), поскольку листьев не более чем n + 1. [[5]](https://stackoverflow.com/questions/9452701/ukkonens-suffix-tree-algorithm-in-plain-english/9513423#9513423)\n\nТеперь когда мы знакомы с базовым материалом, мы можем приступить собственно к самому алгоритму.\n## Определение\n\u003e __Алгоритм Укконена__ (англ. Ukkonen's algorithm) — алгоритм построения суффиксного дерева для заданной строки s за линейное время.\n\nАлгоритм построения суффиксного дерева за линейное время был придуман финским математиком Укконеном (Ukkonen) в 1995 году.  [[14]](https://www.hse.ru/data/2013/02/17/1308149995/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B5%20%D0%90%D0%BD%D0%BD%D0%BE%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D1%8B%D0%B5%20%D0%A1%D1%83%D1%84%D1%84%D0%B8%D0%BA%D1%81%D0%BD%D1%8B%D0%B5%20%D0%94%D0%B5%D1%80..%D0%BE%D0%B1%D0%B5%D0%BD%D0%BD%D0%BE%D1%81%D1%82%D0%B8%20%D1%80%D0%B5%D0%B0%D0%BB%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8%20%28%D0%94%D1%83%D0%B1%D0%BE%D0%B2%20%D0%9C.%29.pdf)\n\nАлгоритм Укконена строит суффиксное дерево, добавляя в него по одной букве. Текущая позиция в дереве соответствует максимальному неполному суффиксу уже добавленных букв, который уже встечался где-то раньше. [[6]](https://en.wikipedia.org/wiki/Ukkonen%27s_algorithm)\n\n## Построение дерева за линейное время\n### Строка без повторений\nСамый простой пример для построения дерева - строка без повторений.\n\u003e abc\n\nАлгоритм работает пошагово, проходя по строке слева направо. Один шаг на каждый символ строки. \nКаждый шаг может включать в себя больше чем одну операцию.  \n\nПостроение начинается слева и сначала вставляется одиночный символ **a** , создавая ребро из корня к листу и хранится как  **[0, #]** - это означает что ребро представляет собой подстроку начинающуюся с позиции 0 и заканчивающуюся на текущем конце. Символ **#** означает индекс за символом (грубо говоря конец символа), то есть на данный момент # = 1;  \n\nНа данный момент дерево выглядит так: \n\n![Дерево для строки \"a\"](/images/img1_1.jpg)\n![Дерево для строки \"a\"](/images/img1_0.jpg)  \n_Рис.1,2 Дерево для строки \"a\"_  \n\nБыл вставлен символ **а**, алгоритм переходит к следующему символу.  \n**Цель: на каждом шаге вставлять все суффиксы до текущей позиции**  \nДелается это с помощью расширения существующих ребер и добавлении нового ребра.  \n\nТеперь дерево имеет вид:  \n\n![Дерево для строки \"ab\"](/images/img2_1.jpg)\n![Дерево для строки \"ab\"](/images/img2_0.jpg)  \n_Рис.3,4 Дерево для строки \"ab\"_\n\n\n\u003eЗаметим, что ребро **ab** такое же как и ребро **a** из рисунков выше и хранится как **[0, #]**, так произошло из-за того что **#** теперь равен **2**.\n\nДобавляется символ **с**:  \n\n![Дерево для строки \"abc\"](/images/img3_1.jpg)\n![Дерево для строки \"abc\"](/images/img3_0.jpg)  \n_Рис.5,6 Дерево для строки \"abc\"_\n\nТак строится дерево, которое не имеет повторяющихся символов.\n\n## Строка с повторениями\n\n\u003eabcabxabz\n\nТак как дерево для строки **abc** было изображено выше, просто продолжим строить его.  \nДобавляется следующий символ **a** и дерево принимает вид:  \n\n![Дерево для строки \"abca\"](/images/img4_0.jpg)  \n_Рис.7 Дерево для строки \"abca\"_\n\n\nТеперь # = 4. Все ребра дерева расширились, но добавлять новое ребро не стоит.  \nЕсть ребро, которое начинается на символ **a**.  \nВ таких случаях вместо добавления нового ребра, алгоритм проходит по уже существующему.  \nПрежде чем сделать это, вводятся еще четыре переменные (в дополнение к #), которые, конечно, существовали всё время, но до сих пор не использовались:\n- активная вершина - вершина из которой выходит ребро\n- активное ребро - индекс первого символа ребра по которому мы будем спускаться\n- активная длина - количество символов, которое мы прошли по ребру (насколько мы спустились по ребру)\n- остаток - количество суффиксов которые осталось добавить.\n \n\u003e Как только мы спустились по ребру, наши переменные имеют значения:\n\u003e - активная вершина = корень\n\u003e - активное ребро = 3 (потому что первый символ в ребре равен символу который мы пытались добавить, а его индекс в свою очередь равен трём.)\n\u003e - активная длина = 1 (спустились по ребру на один символ, можно заметить на фото: наша позиция отмечена чертой)\n\u003e - остаток = 1\n\nСледующий шаг: добавляется символ **b**.   \nПеременная # = 5, заметим, что активная позиция была внутри ребра, символ который добавляется уже есть в этом ребре, поэтому спуск по ребру продолжается. При этом остаток становится = 2.\n\u003e Cуффикс **a** из предыдущего шага никогда не был вставлен должным образом.  \nОн остался, и поскольку алгоритм продвинулся на один шаг, он вырос с а до ab.\n\u003e И необходимо вставить новое финальное ребро b.\n\n![Дерево для строки \"abcab\"](/images/img4_1.jpg)  \n_Рис.8 Дерево для строки \"abcab\"_\n\n\nДобавляется следующий символ **x**. Переменная # = 6.  \nНа рисунке выше видно, что спуск по ребру невозможен, так как символа **х** нет.\nПоэтому ребро делится и подвешивается новая вершина. Теперь дерево имеет вид:\n\n![Дерево для строки \"abcabx\"](/images/img4_2.jpg)    \n_Рис.9 Построение дерева для строки \"abcabx\". Проход по ребру_\n\n\nТеперь нужно вставить оставшиеся суффиксы. Но прежде чем сделать это, необходимо обновить активную позицию.\nПосле каждого разделения, в случае если активной вершиной является корень, переменные изменятся:\n\u003e - активная вершина остается корнем  \n\u003e - активное ребро увеличивается на один (ищется ребро со след. символом, в данном случае b)  \n\u003e - активная длина уменьшается на один (т.к один суффикс уже добавлен)\n\nИтак, активная позиция снова внутри ребра, осталось добавить 2 суффикса, спуск дальше невозможен, поэтому ребро снова делится:\n\n![Дерево для строки \"abcabx\"](/images/img4_3.jpg)  \n_Рис.10 Построение дерева для строки \"abcabx\". Расширение_\n\n\n\u003e Если ребро разделяется и вставляется новая вершина, и если это не первая вершина, созданная на текущем шаге, ранее вставленная вершина и новая вершина соединяются через специальный указатель, **суффиксную ссылку**\n\nВторой суффикс добавлен, теперь остаток = 1. Так как активная позиция - корень, то просто добавляется новое ребро **х**. Остаток = 0.  \n\n![Дерево для строки \"abcabx\"](/images/img4_4.jpg)    \n_Рис.11 Дерево для строки \"abcabx\"_\n\n\nНа данный момент имеется дерево для строки **abcabx**.  \nПропустим следующие два шага с добавлением символом a и b, так как на них мы просто спускаемся по ребру и приходим к его концу.\nПеременная # равна 7, а остаток = 2: \n\n![Дерево для строки \"abcabxab\"](/images/img4_5.jpg)  \n_Рис.12 Дерево для строки \"abcabx\", активная вершина 4_\n\nНа следующем шаге, переменная # = 8, добавляется символ **z**, так как ребра начинающегося на данный символ нет - создадается новое ребро и осуществляется переход по суффиксной ссылке.\n\u003e После разделения или добавления ребра из активной вершины, которая не является корнем, нужно перейти по суффиксной ссылке, выходящей из этой вершины, если таковая имеется. Если суффиксная ссылка отсутствует, активная вершина устанавливается корнем. активное ребро и активная длина остаются без изменений.  \n\n![Дерево для строки \"abcabxab\"](/images/img4_6.jpg)  \n_Рис.13 Построение дерева для строки \"abcabxabz\"_\n\nТак как остаток = 1, в активную вершину добавляется новое ребро с соответствующим символом:\n\n![Дерево для строки \"abcabxab\"](/images/img4_7.jpg)  \n_Рис.13 Построение дерева для строки \"abcabxabz\". Переход по суффиксной ссылке_  \n\nСледующим шагом будет добавление нового ребра **z** и **$**, переменная # = 9:\n\n![Дерево для строки \"abcabxabz$\"](/images/img4_8.jpg)  \n_Рис.13 Дерево для строки \"abcabxabz$\"_  \n\n\u003e Так как дерево должно содержать все суффиксы строки, причем каждый суффикс заканчивается точно в листе и нигде кроме него, используется специальный символ $.\n\u003e Любой суффикс строки с защитным символом действительно заканчивается в листе и только в листе, т. к. в такой строке не существует двух различных подстрок одинаковой длины, заканчивающихся на $.\n\nНа этом построение дерева для строки закончено.\n\n## Теория\n### Правила продления суффиксов\nПусть _S[j..i]_ = β — суффикс _S[1..i]_. В продолжении j, когда алгоритм находит конец β в текущем дереве, он продолжает β, чтобы обеспечить присутствие суффикса _βS(i + 1)_ в дереве. Алгоритм действует по одному из следующих трех правил.\n\n__Правило 1.__ В текущем дереве путь β кончается в листе. Это значит, что путь от корня с меткой β доходит до конца некоторой «листовой» дуги (дуги, входящей в лист). При изменении дерева нужно добавить к концу метки этой листовой дуги символ _S(i + 1)_.  \n\n__Правило 2.__ Ни один путь из конца строки β не начинается символом _S(i + 1)_, но по крайней мере один начинающийся оттуда путь имеется. В этом случае должна быть создана новая листовая дуга, начинающаяся в конце β, помеченная символом _S(i + 1)_. При этом, если β кончается внутри дуги, должна быть создана новая вершина. Листу в конце новой листовой дуги сопоставляется номер j. Таким образом, в правиле 2 возможно два случая.  \n\n__Правило 3.__ Некоторый путь из конца строки β начинается символом _S(i + 1)_. В этом случае строка _βS(i + 1)_ уже имеется в текущем дереве, так что ничего не надо делать (в неявном суффиксном дереве конец суффикса не нужно помечать явно). [[2]](https://vk.cc/cj1OgK)\n### Наивный метод O(n³)\nАлгоритм последовательно строит неявные суффиксные деревья_ для всех префиксов исходного текста _S = s₁s₂…sₙ_. \nНа i-ой фазе неявное суффиксное дерево tᵢ-₁ для префикса _s[1…i − 1]_ достраивается до tᵢ для префикса _s[1…i]_. \nДостраивание происходит следующим образом: для каждого суффикса подстроки _s[1…i − 1]_ необходимо спуститься от корня дерева до конца этого суффикса и дописать символ sᵢ.  \n\nАлгоритм состоит из n фаз. На каждой фазе происходит продление всех суффиксов текущего префикса строки, что требует O(n²) времени. Следовательно, общая асимптотика алгоритма составляет O(n³). [[2]](https://vk.cc/cj1OgK)\n\n\u003e__Неявное суффиксное дерево__ (англ. implicit suffix tree, IST) — это суффиксное дерево, построенное для строки S без добавления $.\n### Суффиксные ссылки\n\u003e Пусть xA обозначает произвольную строку, где x — её первый символ, а A — оставшаяся подстрока (возможно пустая). Если для внутренней вершины _v_ с ребром xА существует другая вершина _s(v)_ с ребром α, то ссылка из _v_ в _s(v)_ называется __суффиксной ссылкой__ (англ. suffix link).\n#### Построение суффиксных ссылок\nРассмотрим новую внутреннюю вершину _v_, которая была создана в результате продления суффикса _s[j…i − 1]_. \nВместо того, чтобы искать, куда должна указывать суффиксная ссылка вершины _v_, поднимаясь от корня дерева для этого, перейдем к продлению следующего суффикса _s[j+1…i−1]_. И в этот момент можно проставить суффиксную ссылку для вершины _v_. \nОна будет указывать либо на существующую вершину, если следующий суффикс закончился в ней, либо на новую созданную. То есть суффиксные ссылки будут обновляться с запаздыванием. [[1]](https://vk.cc/cj1OgK)\n#### Использование суффиксных ссылок\nПусть только что был продлён суффикс _[j…i − 1]_ до суффикса _s[j…i]_.  \nТеперь с помощью построенных ссылок можно найти конец суффикса _s[j + 1…i − 1]_ в суффиксном дереве, чтобы продлить его до суффикса _s[j + 1…i]_. Для этого надо пройти вверх по дереву до ближайшей внутренней вершины _v_, в которую ведёт путь, помеченный _s[j…r]_. У вершины _v_ точно есть суффиксная ссылка. Эта суффиксная ссылка ведёт в вершину _u_, которой соответствует путь, помеченный подстрокой _s[j + 1…r]_. Теперь от вершины u следует пройти вниз по дереву к концу суффикса _s[j + 1…i − 1]_ и продлить его до суффикса _s[j + 1…i]_.\n\nПодстрока _s[j + 1…i − 1]_ является суффиксом подстроки _s[j…i − 1]_, следовательно после перехода по суффиксной ссылке в вершину, помеченную путевой меткой _s[j + 1…r]_, можно дойти до места, которому соответствует метка _s[r + 1…i − 1]_, сравнивая не символы на рёбрах, а лишь длину ребра по первому символу рассматриваемой части подстроки и длину самой этой подстроки. Таким образом можно спускаться вниз сразу на целое ребро.\n#### Первая лемма\nПри переходе по суффиксной ссылке глубина уменьшается не более чем на 1.\n\u003e_Глубиной вершины назовем число рёбер на пути от корня до вершины _v__.\n\n__Доказательство__:\nЗаметим, что на пути A в дереве по суффиксу _s[j + 1…i]_ не более чем на одну вершину меньше, чем на пути B по суффиксу _s[j…i]_. Каждой вершине _v_ на пути B соответствует вершина _u_ на пути A, в которую ведёт суффиксная ссылка. Разница в одну вершину возникает, если первому ребру в пути B соответсвует метка из одного символа _sⱼ_, тогда суффиксная ссылка из вершины, в которую ведёт это ребро, будет вести в корень.\n#### Вторая лемма\nЧисло переходов по рёбрам внутри фазы номер i равно O(i).  \n\n__Доказательство__:\nОценим количество переходов по рёбрам при поиске конца суффикса. Переход до ближайшей внутренней вершины уменьшает высоту на 1. Переход по суффиксной ссылке уменьшает высоту не более чем на 1 (по лемме, доказанной выше). А потом высота увеличивается, пока мы переходим по рёбрам вниз. Так как высота не может увеличиваться больше глубины дерева, а на каждой j-ой итерации мы уменьшаем высоту не более, чем на 2, то суммарно высота не может увеличиться больше чем на 2i. Итого, число переходов по рёбрам за одну фазу в сумме составляет O(i).\n\n#### Асимптотика алгоритма с использованием суффиксных ссылок\nТеперь в начале каждой фазы мы только один раз спускаемся от корня, а дальше используем переходы по суффиксным ссылкам.   \nПо доказанной лемме переходов внутри фазы будет O(i). А так как фаза состоит из i итераций, то амортизационно получаем, что на одной итерации будет выполнено O(1) действий.  \nСледовательно, асимптотика алгоритма улучшилась до O(n²).\n\n### Алгоритм за O(n)\nЧтобы улучшить время работы данного алгоритма до O(n), нужно использовать линейное количество памяти, поэтому метка каждого ребра будет храниться как два числа — позиции её самого левого и самого правого символов в исходном тексте.\n#### Итоговая оценка времени работы\nВ течение работы алгоритма создается не более O(n) вершин по лемме о размере суффиксного дерева для строки. Все суффиксы, которые заканчиваются в листах, благодаря первой лемме на каждой итерации мы увеличиваем на текущий символ по умолчанию за O(1). Текущая фаза алгоритма будет продолжаться, пока не будет использовано правило продления 3.  \nСначала неявно продлятся все листовые суффиксы, а потом по правилу 2 будет создано несколько новых внутренних вершин. Так как вершин не может быть создано больше, чем их есть, то амортизационно на каждой фазе будет создано O(1) вершин. Так как мы на каждой фазе начинаем добавление суффикса не с корня, а с индекса j∗, на котором в прошлой фазе было применено правило 3, то используя немного модифицированный вариант леммы о числе переходов внутри фазы нетрудно показать, что суммарное число переходов по рёбрам за все n фаз равно O(n).\n\nТаким образом, при использовании всех приведённых эвристик алгоритм Укконена работает за O(n). [[2]](https://vk.cc/cj1OgK)\n## Тестирование\nТестирование запускается функцией __test_tree()__;\n### Входные данные\nФормат входных данных:  \nНа вход подается файл - номер_теста\n\u003e Строка S (пример: \"abcxab\")  \n\u003e Подстрока строки S (пример: \"xab\")  \n\u003e Вхождение подстроки в строку S (пример: 3)  \n\nФормат выходных данных:\nНа выходе создается файл - result   \n\n### Выходные данные\nТестирование считается успешным, если все тесты были пройдены.\n\u003e test #1 - ok  \n\u003e test #2 - ok  \n\u003e test #3 - ok  \n\u003e ...  \n\u003e test #21 - ok  \n\u003e \n\u003e GENERATED TESTS   \n\u003e seed: *random_seed*  \n\u003e \n\u003e test #1 - ok  \n\u003e test #2 - ok  \n\u003e test #3 - ok  \n\u003e ...  \n\u003e test #100 - ok\n\nЕсли тест был провален, дополнительно выводится строка, подстрока, ответ и вывод программы.\n\u003e test #1 - ok  \n\u003e test #2 - ok  \n\u003e ...  \n\u003e test #x - failed  \n\u003e ㅤㅤstr: abcxab   \n\u003e ㅤㅤsubstr: xab  \n\u003e ㅤㅤanswer: 3  \n\u003e ㅤㅤoutput: 2\n\nВ таком случае тестирование прекращается, программа завершается с ошибкой -1;\n## Анализ производительности\n### Функция построения дерева  \nЗамеры производились для строк длиной до 5000 символов.  \n\n![Дерево для строки \"abca\"](/images/img5_0.jpg)  \n\n### Функция поиска подстроки\nВ замерах использовались:   \n__std::string.find()__   \n__suffix_tree::find()__  \n\nПоиск случайной подстроки в строке длиной до 5000.  \n\n![Дерево для строки \"abca\"](/images/img5_1.jpg)\n\nПоиск подстроки длиной 5 с конца строки.\n\n![Дерево для строки \"abca\"](/images/img5_2.jpg)\n\n### Вывод\nДанный алгоритм в среднем уступает более быстрым алгоритмам (std::string.find()), поэтому его использование рекомендуется только в исключительных случаях. Как пример, если подстрока находится в конце строки.\n\n## Список литературы\n- [1] [Простое суффиксное дерево / Хабр](https://habr.com/ru/post/258121/)\n- [2] [Алгоритм Укконена — Викиконспекты](https://vk.cc/cj1OgK)\n- [3] [АиСД S03E12. Суффиксное дерево. Алгоритм Укконена](https://www.youtube.com/watch?v=WjzR1eFbAeo\u0026t=1328s\u0026ab_channel=PavelMavrin)\n- [4] [Visualization of Ukkonen's Algorithm](http://brenden.github.io/ukkonen-animation/)\n- [5] [Ukkonen's suffix tree algorithm in plain English / stackoverflow](https://stackoverflow.com/questions/9452701/ukkonens-suffix-tree-algorithm-in-plain-english/9513423#9513423)\n- [6] [Ukkonen's algorithm - Wikipedia](https://en.wikipedia.org/wiki/Ukkonen%27s_algorithm)\n- [7] [Suffix Tree Application 1 - Substring Check - GeeksforGeeks](https://www.geeksforgeeks.org/suffix-tree-application-1-substring-check/)\n- [8] [Linear Time Construction of Suffix Trees with Ukkonen's Algorithm](https://www.youtube.com/watch?v=OT5CigmVfh0\u0026ab_channel=%E8%9C%BB%E8%9B%89)\n- [9] [Suffix Tree-Ukkonen's Algorithm - Coding Ninjas CodeStudio](https://www.codingninjas.com/codestudio/library/suffix-tree-ukkonens-algorithm)\n- [10] [Chapter on suffix trees - CMU School of Computer Science](https://www.cs.cmu.edu/afs/cs/project/pscico-guyb/realworld/www/slidesF06/cmuonly/gusfield.pdf)\n- [11] [A\u0026DS S03E13. Suffix Tree. Ukkonen's Algorithm](https://www.youtube.com/watch?v=C10HoshM_DA\u0026ab_channel=PavelMavrin)\n- [12] [Suffix Tree using Ukkonen's algorithm](https://www.youtube.com/watch?v=aPRqocoBsFQ\u0026ab_channel=TusharRoy-CodingMadeSimple)\n- [13] [Суффиксные деревья, алгоритм Укконена. Сжатие данных](https://users.math-cs.spbu.ru/~okhotin/teaching/tcs2_2019/okhotin_tcs2alg_2019_l3.pdf)\n- [14] [Обобщенные аннотированные суффиксные деревья](https://www.hse.ru/data/2013/02/17/1308149995/%D0%9E%D0%B1%D0%BE%D0%B1%D1%89%D0%B5%D0%BD%D0%BD%D1%8B%D0%B5%20%D0%90%D0%BD%D0%BD%D0%BE%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D1%8B%D0%B5%20%D0%A1%D1%83%D1%84%D1%84%D0%B8%D0%BA%D1%81%D0%BD%D1%8B%D0%B5%20%D0%94%D0%B5%D1%80..%D0%BE%D0%B1%D0%B5%D0%BD%D0%BD%D0%BE%D1%81%D1%82%D0%B8%20%D1%80%D0%B5%D0%B0%D0%BB%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D0%B8%20%28%D0%94%D1%83%D0%B1%D0%BE%D0%B2%20%D0%9C.%29.pdf)\n- [15] [Алгоритм Укконена на пальцах](http://www.proteus2001.narod.ru/gen/txt/10/ukk.html)\n- [16] [Алгоритм Укконена - frwiki.wiki](https://ru.frwiki.wiki/wiki/Algorithme_d%27Ukkonen)\n- [17][Суффиксное дерево - Википедия](https://ru.wikipedia.org/wiki/%D0%A1%D1%83%D1%84%D1%84%D0%B8%D0%BA%D1%81%D0%BD%D0%BE%D0%B5_%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE)\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flonelywh1te%2Fukkonen-algorithm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flonelywh1te%2Fukkonen-algorithm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flonelywh1te%2Fukkonen-algorithm/lists"}