Опечатки, баги и конструктивная критика принимаются (Антон Тимофеев).
Самая простая структура вершины:
struct Vertex
{
static const int alphabet = 256;
Vertex* to[alphabet]; // указатели на смежные вершины, NULL - если нет ребра
Vertex() { memset(to, 0, sizeof(NULL)); } // конструктор пустой новой вершины
};
Если хотим cжать бор, убрав лишние ребра в вершинах со степенями 0 и 1, то структура следующая:
struct Vertex
{
static const int alphabet = 256;
char prevc; // символ, по которому мы пришли из родителя
int degree; // степень вершины 0, 1 или alphabet
Vertex** to; // degree == 0 => NULL
// degree == 1 => (Vertex*)to - указатель на смежную вершину
// degree >= 2 => to - указатель на первую вершину в массиве указателей на смежные
Vertex( char prevc ) : prevc(prevc), degree(0), to(NULL) { }
/* Переход по символу c, равно NULL, если не существует перехода */
Vertex* To( char c )
{
if (degree == 0)
return NULL;
if (degree == 1)
return (Vertex *)to->prevc == c ? (Vertex*)to : NULL;
return to[c];
}
/* Переход по символу, создающий новое ребро, если ещё не было */
Vertex* Access( char c )
{
if (degree == 0)
{
to = new Vertex(c);
degree++;
return to;
}
if (degree == 1)
{
if ((Vertex*)to->prevc == c)
return (Vertex*)to;
Vertex** edges = new Vertex*[alphabet];
edges[(Vertex*)to->prevc] = (Vertex*)to;
to = edges;
}
if (to[c] == NULL)
to[c] = new Vertex(c);
return to[c];
}
};
PS: Петя был прав на лекции про массив указателей на вершины. Если создавать просто массив вершин, мы сразу добавим в бор |Σ| новых развилок, абсолютно не нужных, что не правильно.
Сжимать бор c Ахо-Корасик бесполезно, если хранить все функции переходов next(v, c) для каждой вершины (всё равно тратим O(|Σ|·Σsi) памяти).
struct Vertex
{
const int alphabet = 256;
Vertex* prev; // ссылка на родителя
char prevc; // символ, по которому мы пришли из родителя
Vertex* to[alphabet]; // указатели на смежные вершины, NULL - если нет ребра
Vertex* suff; // суффиксная ссылка, здесь хранится предподсчёт для Suff(v)
Vertex* next[alphabet]; // предподсчёт для функции переходов Next(v, c)
Vertex( Vertex* prev, char prevc ) : prev(prev), prevc(prevc), suff(NULL)
{
memset(to, 0, sizeof(to));
memset(next, 0, sizeof(next));
}
/* Переход по символу, создающий новое ребро, если ещё не было */
Vertex* Access( char c )
{
if (to[c] == NULL)
to[c] = new Vertex(this, c);
return to[c];
}
/* Вычисление суффиксной ссылки с запоминанием */
Vertex* Suff()
{
if (suff != NULL)
return suff;
return suff = prev->Suff()->Next(v->prevc);
}
/* Функция перехода с запоминанием */
Vertex* Next( char c )
{
if (next[c] != NULL)
return next[c];
if (to[c] != NULL)
return next[c] = to[c];
return next[c] = Suff()->Next(c);
}
};
Перед использованием методов Suff и Next необходимо вызвать от построенного бора функцию Init(root).
void Init( Vertex*& root )
{
assert(root != NULL);
root->suff = root;
for (int i = 0; i < root->alphabet; i++)
if (root->to[i] != NULL)
root->to[i]->suff = root;
for (int i = 0; i < root->alphabet; i++)
root->next[i] = root->to[i] != NULL ? root->to[i] : root;
}