<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ru">
		<id>http://neerc.ifmo.ru/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=217.66.159.55&amp;*</id>
		<title>Викиконспекты - Вклад участника [ru]</title>
		<link rel="self" type="application/atom+xml" href="http://neerc.ifmo.ru/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=217.66.159.55&amp;*"/>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%BB%D1%83%D0%B6%D0%B5%D0%B1%D0%BD%D0%B0%D1%8F:%D0%92%D0%BA%D0%BB%D0%B0%D0%B4/217.66.159.55"/>
		<updated>2026-06-14T11:51:47Z</updated>
		<subtitle>Вклад участника</subtitle>
		<generator>MediaWiki 1.30.0</generator>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%90%D1%85%D0%BE-%D0%9A%D0%BE%D1%80%D0%B0%D1%81%D0%B8%D0%BA&amp;diff=38280</id>
		<title>Алгоритм Ахо-Корасик</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%90%D1%85%D0%BE-%D0%9A%D0%BE%D1%80%D0%B0%D1%81%D0%B8%D0%BA&amp;diff=38280"/>
				<updated>2014-06-10T10:28:42Z</updated>
		
		<summary type="html">&lt;p&gt;217.66.159.55: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Задача алгоритма ==&lt;br /&gt;
Найти для каждого образца из заданного множества образцов (размером &amp;lt;tex&amp;gt;k&amp;lt;/tex&amp;gt;) все его вхождения в текст за время &amp;lt;tex&amp;gt;O(m+n+a)&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;m&amp;lt;/tex&amp;gt; {{---}} суммарная длина образцов, &amp;lt;tex&amp;gt;n&amp;lt;/tex&amp;gt; {{---}} длина текста, &amp;lt;tex&amp;gt;a&amp;lt;/tex&amp;gt; {{---}} размер ответа (количество пар). В худшем случае &amp;lt;tex&amp;gt;a=nk&amp;lt;/tex&amp;gt;, но на практике он встречается редко.&lt;br /&gt;
&lt;br /&gt;
== Шаг 1 ==&lt;br /&gt;
Строим [[Бор|бор]] из образцов.&amp;lt;br /&amp;gt;&lt;br /&gt;
Построение выполняется за время &amp;lt;tex&amp;gt;O(m)&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;m&amp;lt;/tex&amp;gt; {{---}} суммарная длина образцов.&lt;br /&gt;
&lt;br /&gt;
=== Пример построенного бора ===&lt;br /&gt;
Бор для набора образцов {he, she, his, hers}:&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Файл:Aho-corasick1.jpg‎]]&lt;br /&gt;
&lt;br /&gt;
== Шаг 2 ==&lt;br /&gt;
Превращаем бор в автомат.&amp;lt;br /&amp;gt;&lt;br /&gt;
Узлы бора становятся состояниями автомата; корень {{---}} начальное состояние.&amp;lt;br /&amp;gt;&lt;br /&gt;
Узлы бора, в которых заканчиваются образцы, становятся терминалами.&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
Для переходов по автомату заведём в узлах несколько функций:&amp;lt;br /&amp;gt;&lt;br /&gt;
1) &amp;lt;tex&amp;gt;parent(u)&amp;lt;/tex&amp;gt; {{---}} возвращает родителя вершины &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;;&amp;lt;br /&amp;gt;&lt;br /&gt;
2) &amp;lt;tex&amp;gt;\pi(u) = \delta(\pi(parent(u)), c)&amp;lt;/tex&amp;gt; {{---}} суффиксная ссылка; здесь &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; {{---}} сын &amp;lt;tex&amp;gt;parent(u)&amp;lt;/tex&amp;gt; по символу &amp;lt;tex&amp;gt;c&amp;lt;/tex&amp;gt;;&amp;lt;br /&amp;gt;&lt;br /&gt;
3) &amp;lt;tex&amp;gt;\delta(u, c) = &lt;br /&gt;
  \begin{cases}&lt;br /&gt;
     v\text{, if $v$ is son by symbol $c$ in trie;}\\&lt;br /&gt;
     root\text{, if $u$ is root and $u$ has no child by symbol $c$ in trie }\\&lt;br /&gt;
     \delta(\pi(u), c)\text{, else.}&lt;br /&gt;
   \end{cases}&amp;lt;/tex&amp;gt; {{---}} функция перехода.&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
Суффиксная ссылка &amp;lt;tex&amp;gt;\pi(u) = v&amp;lt;/tex&amp;gt;, если &amp;lt;tex&amp;gt;[v]&amp;lt;/tex&amp;gt; {{---}} максимальный суффикс &amp;lt;tex&amp;gt;[u]&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;[v]\neq[u]&amp;lt;/tex&amp;gt;. Обозначение: &amp;lt;tex&amp;gt;[u]&amp;lt;/tex&amp;gt; {{---}} слово, приводящее в вершину &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; в боре.&amp;lt;br /&amp;gt;&lt;br /&gt;
Функции перехода и суффиксные ссылки можно найти либо алгоритмом обхода в глубину с ленивыми вычислениями, либо с помощью алгоритма обхода в ширину.&lt;br /&gt;
=== Пример автомата Ахо-Корасик ===&lt;br /&gt;
[[Файл:Aho-corasick2.jpg]]&amp;lt;br /&amp;gt;&lt;br /&gt;
Пунктиром обозначены суффиксные ссылки. Из вершин, для которых они не показаны, суффиксные ссылки идут в корень.&lt;br /&gt;
&lt;br /&gt;
== Шаг 3 ==&lt;br /&gt;
Построение сжатых суффиксных ссылок.&amp;lt;br /&amp;gt;&lt;br /&gt;
&amp;lt;tex&amp;gt;up(u) = &lt;br /&gt;
  \begin{cases}&lt;br /&gt;
    \pi(u)\text{, if $\pi(u)$ is terminal;}\\&lt;br /&gt;
    \emptyset\text{, if $\pi(u)$ is root;}\\&lt;br /&gt;
    up(\pi(u))\text{, else.}&lt;br /&gt;
  \end{cases}&amp;lt;/tex&amp;gt; {{---}} сжатая суффиксная ссылка, т.е. ближайшее допускающее состояние (терминал) перехода по суффиксным ссылкам.&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
Сжатые суффиксные ссылки могут отыскиваться при помощи ленивой рекурсии.&lt;br /&gt;
&lt;br /&gt;
== Использование автомата ==&lt;br /&gt;
По очереди просматриваем символы текста. Для очередного символа &amp;lt;tex&amp;gt;c&amp;lt;/tex&amp;gt; переходим из текущего состояния &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; в состояние, которое вернёт функция &amp;lt;tex&amp;gt;\delta(u, c)&amp;lt;/tex&amp;gt;. Оказавшись в новом состоянии, отмечаем по сжатым суффиксным ссылкам образцы, которые нам встретились и их позицию (если требуется). Если новое состояние является терминалом, то соответствующие ему образцы тоже отмечаем.&amp;lt;br /&amp;gt;&lt;br /&gt;
''Примечание.'' Если требуется найти только первое вхождение образца в текст, то существенно ускорить работу алгоритма могут пометки о посещённости узла, т.е. если узел посещён, то не переходить по сжатым суффиксным ссылкам. Вместо хранения пометок можно просто сбрасывать сжатую суффиксную ссылку.&lt;br /&gt;
&lt;br /&gt;
== Пример реализации ==&lt;br /&gt;
Ниже представлена реализация на C++ некоторых функций (используется ленивая рекурсия).&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
'''Структура вершины:'''&lt;br /&gt;
 struct Node {&lt;br /&gt;
     Node* son[SZ];        &amp;lt;font color=green&amp;gt;// массив сыновей; SZ - это размер алфавита&amp;lt;/font&amp;gt;&lt;br /&gt;
     Node* go[SZ];         &amp;lt;font color=green&amp;gt;// массив переходов (запоминаем переходы в ленивой рекурсии)&amp;lt;/font&amp;gt;&lt;br /&gt;
     Node* parent;         &amp;lt;font color=green&amp;gt;// вершина родитель&amp;lt;/font&amp;gt;&lt;br /&gt;
     Node* suffLink;       &amp;lt;font color=green&amp;gt;// суффиксная ссылка (вычисляем в ленивой рекурсии)&amp;lt;/font&amp;gt;&lt;br /&gt;
     Node* up;             &amp;lt;font color=green&amp;gt;// сжатая суффиксная ссылка&amp;lt;/font&amp;gt;&lt;br /&gt;
     char charToParent;    &amp;lt;font color=green&amp;gt;// символ, ведущий к родителю&amp;lt;/font&amp;gt;&lt;br /&gt;
     bool leaf;            &amp;lt;font color=green&amp;gt;// флаг, является ли вершина терминалом&amp;lt;/font&amp;gt;&lt;br /&gt;
     std::vector&amp;lt;int&amp;gt; leafPatternNumber;   &amp;lt;font color=green&amp;gt;// номера образцов, за которые отвечает терминал&amp;lt;/font&amp;gt;&lt;br /&gt;
 };&lt;br /&gt;
'''Функция, для вычисления суффиксной ссылки:'''&lt;br /&gt;
 Node* getSuffLink(Node* v) {&lt;br /&gt;
     if (!v-&amp;gt;suffLink) {   &amp;lt;font color=green&amp;gt;// если суффиксная ссылка ещё не вычислена&amp;lt;/font&amp;gt;&lt;br /&gt;
         if (v == root || v-&amp;gt;parent == root) {&lt;br /&gt;
             v-&amp;gt;suffLink = root;&lt;br /&gt;
         } else {&lt;br /&gt;
             v-&amp;gt;suffLink = getGo(getSuffLink(v-&amp;gt;parent), v-&amp;gt;charToParent);&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
     return v-&amp;gt;suffLink;&lt;br /&gt;
 }&lt;br /&gt;
'''Функция, для вычисления перехода:'''&lt;br /&gt;
 Node* getGo(Node* v, char c) {&lt;br /&gt;
     if (!v-&amp;gt;go[c]) {      &amp;lt;font color=green&amp;gt;// если переход по символу c ещё не вычислен&amp;lt;/font&amp;gt;&lt;br /&gt;
         if (v-&amp;gt;son[c]) {&lt;br /&gt;
             v-&amp;gt;go[c] = v-&amp;gt;son[c];&lt;br /&gt;
         } else {&lt;br /&gt;
             v-&amp;gt;go[c] = (v == root) ? root : getGo(getSuffLink(v), c);&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
     return v-&amp;gt;go[c];&lt;br /&gt;
 }&lt;br /&gt;
'''Функция, для вычисления сжатой суффиксной ссылки:'''&lt;br /&gt;
 Node* getUp(Node* v) {&lt;br /&gt;
     if (!v-&amp;gt;up) {         &amp;lt;font color=green&amp;gt;// если сжатая суффиксная ссылка ещё не вычислена&amp;lt;/font&amp;gt;&lt;br /&gt;
         if (getSuffLink(v)-&amp;gt;leaf) {&lt;br /&gt;
             v-&amp;gt;up = getSuffLink(v);&lt;br /&gt;
         } else if (getSuffLink(v) == root) {&lt;br /&gt;
             v-&amp;gt;up = root;&lt;br /&gt;
         } else {&lt;br /&gt;
             v-&amp;gt;up = getUp(getSuffLink(v));&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
     return v-&amp;gt;up;&lt;br /&gt;
 }&lt;br /&gt;
'''Функция, для добавление образца в бор:'''&lt;br /&gt;
 void addString(std::string const&amp;amp; s, int patternNumber) {&lt;br /&gt;
     Node* cur = root;&lt;br /&gt;
     for (int i = 0; i &amp;lt; s.length(); ++i) {&lt;br /&gt;
         char c = s[i] - 'a';&lt;br /&gt;
         if (cur-&amp;gt;son[c] == 0) {&lt;br /&gt;
             cur-&amp;gt;son[c] = new Node;&lt;br /&gt;
             &amp;lt;font color=green&amp;gt;/* здесь также нужно обнулить указатели на переходы и сыновей */&amp;lt;/font&amp;gt;&lt;br /&gt;
             cur-&amp;gt;son[c]-&amp;gt;suffLink = 0;&lt;br /&gt;
             cur-&amp;gt;son[c]-&amp;gt;up = 0;&lt;br /&gt;
             cur-&amp;gt;son[c]-&amp;gt;parent = cur;&lt;br /&gt;
             cur-&amp;gt;son[c]-&amp;gt;charToParent = c;&lt;br /&gt;
             cur-&amp;gt;son[c]-&amp;gt;leaf = false;&lt;br /&gt;
         }&lt;br /&gt;
         cur = cur-&amp;gt;son[c];&lt;br /&gt;
     }&lt;br /&gt;
     cur-&amp;gt;leaf = true;&lt;br /&gt;
     cur-&amp;gt;leafPatternNumber.push_back(patternNumber);&lt;br /&gt;
 }&lt;br /&gt;
'''Функция, для процессинга текста (поиск, встречается образец или нет):'''&lt;br /&gt;
 void processText(std::string const&amp;amp; t, std::vector&amp;lt;bool&amp;gt;&amp;amp; found) {   &amp;lt;font color=green&amp;gt;// found - это вектор, длина которого равна количеству образцов&amp;lt;/font&amp;gt;&lt;br /&gt;
     found.assign(w, false);   &amp;lt;font color=green&amp;gt;// w - количество образцов&amp;lt;/font&amp;gt;&lt;br /&gt;
     Node* cur = root;&lt;br /&gt;
     for (int i = 0; i &amp;lt; t.length(); ++i) {&lt;br /&gt;
         char c = t[i] - 'a';&lt;br /&gt;
         cur = getGo(cur, c);&lt;br /&gt;
         for (int j = 0; j &amp;lt; cur-&amp;gt;leafPatternNumber.size(); ++j) {&lt;br /&gt;
             found[cur-&amp;gt;leafPatternNumber[j]] = true;&lt;br /&gt;
         }&lt;br /&gt;
         &amp;lt;font color=green&amp;gt;/* В этом месте кода должен выполняться переход по '''сжатой''' суффиксной ссылке getUp(cur). Для вершины,&lt;br /&gt;
            обнаруженной по ней тоже ставим, что она найдена, затем повторяем для её сжатой суффиксной ссылки&lt;br /&gt;
            и так до корня. Хорошо ускорит программу сброс сжатых суффиксных ссылок для посещённых вершин. */&amp;lt;/font&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
Кроме этих функций требуется инициализация, но она имеет отношение только к кодированию, поэтому здесь приведена не будет.&lt;br /&gt;
&lt;br /&gt;
== Поиск шаблонов с масками ==&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;\phi&amp;lt;/tex&amp;gt; {{---}} маска, обозначающая любой одиночный символ.&amp;lt;BR&amp;gt;&lt;br /&gt;
Например, шаблон &amp;lt;tex&amp;gt;ab\phi\phi c\phi&amp;lt;/tex&amp;gt; встречается на позициях 2 и 8 строки&lt;br /&gt;
&amp;lt;tex&amp;gt;xabvccababcax&amp;lt;/tex&amp;gt;.&amp;lt;BR&amp;gt;&lt;br /&gt;
Если количество &amp;lt;tex&amp;gt;\phi&amp;lt;/tex&amp;gt; ограничено сверху константой, то шаблоны с&lt;br /&gt;
масками могут быть выявлены за линейное время. Для этого надо обнаружить&lt;br /&gt;
безмасочные куски шаблона &amp;lt;tex&amp;gt;Q&amp;lt;/tex&amp;gt;:&amp;lt;BR&amp;gt;&lt;br /&gt;
Пусть {&amp;lt;tex&amp;gt;Q_1&amp;lt;/tex&amp;gt;, ..., &amp;lt;tex&amp;gt;Q_k&amp;lt;/tex&amp;gt;} {{---}} набор подстрок&lt;br /&gt;
&amp;lt;tex&amp;gt;Q&amp;lt;/tex&amp;gt;, разделенных масками, и пусть &amp;lt;tex&amp;gt;l_1&amp;lt;/tex&amp;gt;, ...,&lt;br /&gt;
&amp;lt;tex&amp;gt;l_k&amp;lt;/tex&amp;gt; {{---}} их стартовые позиции в &amp;lt;tex&amp;gt;Q&amp;lt;/tex&amp;gt;.&amp;lt;BR&amp;gt;&lt;br /&gt;
1. &amp;lt;tex&amp;gt;\forall i = 1 \ldots m: C[i] = 0 &amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;m&amp;lt;/tex&amp;gt; {{---}} длина текста. &amp;lt;BR&amp;gt;&lt;br /&gt;
2. Используя АК, находим подстроки &amp;lt;tex&amp;gt;Q&amp;lt;/tex&amp;gt;: когда находим &amp;lt;tex&amp;gt;Q_i&amp;lt;/tex&amp;gt;&lt;br /&gt;
в тексте T на позиции &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, увеличиваем на единицу &amp;lt;tex&amp;gt;C[j - l_i + 1]&amp;lt;/tex&amp;gt;.&amp;lt;BR&amp;gt;&lt;br /&gt;
3. Каждое &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;, для которого &amp;lt;tex&amp;gt;C[i] = k&amp;lt;/tex&amp;gt;, является&lt;br /&gt;
стартовой позицией появления шаблона &amp;lt;tex&amp;gt;Q&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Источники ==&lt;br /&gt;
* http://e-maxx.ru/algo/aho_corasick&lt;br /&gt;
* http://aho-corasick.narod.ru/&lt;br /&gt;
&lt;br /&gt;
[[Категория: Дискретная математика и алгоритмы]] &lt;br /&gt;
[[Категория: Поиск подстроки в строке]]&lt;/div&gt;</summary>
		<author><name>217.66.159.55</name></author>	</entry>

	</feed>