Suffix Tree(后缀树)

Posted darkchii

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Suffix Tree(后缀树)相关的知识,希望对你有一定的参考价值。

  这篇简单的谈谈后缀树原理及实现。

  如前缀树原理一般,后缀trie树是将字符串的每个后缀使用trie树的算法来构造。例如banana的所有后缀:

0: banana
1:  anana
2:   nana
3:    ana
4:     na
5:      a

  按字典序排列后:

5: a
3:  ana
1:     anana
0: banana
4: na
2:   nana

  形成一个树形结构。

  代码实现:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// banana中不重复的字符有:a b n
/*
 *   a   b   n
 *  n $  a   a
 *  a    n  n $
 * n $   a  a
 * a     n  $
 * $     a
         $*/
#define SIZE 27
#define Index(c) ((c) - ‘a‘)
#define rep(i, a, b) for(i = a; i < b; i++)
 
typedef struct BaseNode {
	struct BaseNode*next[SIZE];
	char c;
	int num;
} suffix_tree, *strie;
 
void initialize(strie* root)
{
	int i;
	*root = (strie)malloc(sizeof(suffix_tree));
	(*root)->c = 0;
	rep(i, 0, SIZE) (*root)->next[i] = NULL;
}
 
void insert(strie*root, const char*str)
{
	suffix_tree*node = *root, *tail;
	int i, j;
	for (i = 0; str[i] != ‘‘; i++)
	{
		if (node->next[Index(str[i])] == NULL)
		{
				tail = (strie)malloc(sizeof(suffix_tree));
				tail->c = str[i];
				rep(j, 0, SIZE) tail->next[j] = NULL;
				node->next[Index(str[i])] = tail;
		}
		node = node->next[Index(str[i])];
	}
	rep(i, 0, SIZE) node->next[i] = NULL;
	tail = (strie)malloc(sizeof(suffix_tree));
	tail->c = ‘$‘;
	rep(i, 0, SIZE) tail->next[i] = NULL;
	node->next[SIZE - 1] = tail;
}


 
void show(suffix_tree*root)
{
	if (root)
	{
		int i;
		rep(i, 0, SIZE) show(root->next[i]);
		printf("%c
", root->c);
	}
}
 
void destory(strie*root)
{
	if (*root)
	{
		int i;
		rep(i, 0, SIZE) destory(&(*root)->next[i]);
		free(*root);
		*root = NULL;
	}
}
 
int main()
{
	suffix_tree*root;

	initialize(&root);

	char str[] = "banana", *p = str;

	while(*p)
	{
		insert(&root, p);
		p++;
	}
	show(root);
	destory(&root);
	return 0;
}

  上面算法中对于一串长m的字符串,建立一颗后缀字典树所需的时间为O(m2),27的循环在这里可看作常数,空间复杂度为O(m)。这里虽然也是O(m)的space,但倍数会比较大。

  由于上面算法空间复杂度比较大,所以使用路径压缩以节省空间,这样的树就称为后缀树,也可以通过下标来存储,如图:

技术分享图片

  下面是代码是实现:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
/*
     a    banana   na
    na $    $     na $
   na $            $
    $
*/
#define SIZE 27
#define Index(c) ((c) - ‘a‘)
#define rep(i, a, b) for(i = a; i < b; i++)
 
typedef struct BaseNode {
	struct BaseNode*next[SIZE];
	char c;
	char str[SIZE << 2];
	int num;
} suffix_tree, *strie;
 
void initialize(strie* root)
{
	*root = (strie)malloc(sizeof(suffix_tree));
	(*root)->c = 0;
	memset((*root)->str, 0, sizeof((*root)->str));
	memset((*root)->next, 0, sizeof((*root)->next));
}
 
void insert(strie*root, const char*str)
{
	suffix_tree*node = *root, *tail;
	if (node->next[Index(str[0])] == NULL)
	{
		tail = (strie)malloc(sizeof(suffix_tree));
		tail->c = 0;
		strcpy(tail->str, str);
		memset(tail->next, 0, sizeof(tail->next));
		node->next[Index(str[0])] = tail;
	}
	node = node->next[Index(str[0])];
	tail = (strie)malloc(sizeof(suffix_tree));
	tail->c = ‘$‘;
	memset(tail->str, 0, sizeof(tail->str));
	memset(tail->next, 0, sizeof(tail->next));
	node->next[SIZE - 1] = tail;
}

void show(suffix_tree*root)
{
	if (root)
	{
		int i;
		rep(i, 0, SIZE) show(root->next[i]);
		printf("%s", root->str);
		if(root->c)
			printf("%c", root->c);
		puts("");
	}
}
 
void destory(strie*root)
{
	if (*root)
	{
		int i;
		rep(i, 0, SIZE) destory(&(*root)->next[i]);
		free(*root);
		*root = NULL;
	}
}
 
int main()
{
	suffix_tree*root;

	initialize(&root);

	char str[] = "banana", *p = str;

	while(*p)
	{
		insert(&root, p);
		p++;
	}
	show(root);
	destory(&root);
	return 0;
}

  上面算法建立字符串的后缀树的时间复杂度为O(m),空间复杂度O(m)。

  上面的都是对于一个字符串的处理方法,而广义后缀树将算法推广到了不同的字符串上,但我还没写过。

  参考:https://en.wikipedia.org/wiki/Suffix_tree

以上是关于Suffix Tree(后缀树)的主要内容,如果未能解决你的问题,请参考以下文章

2021CCPC(桂林) - Suffix Automaton(后缀树+线段树)

2021CCPC(桂林) - Suffix Automaton(后缀树+线段树)

字符串-后缀树和后缀数组详解

字符串-后缀树和后缀数组详解

ural1297(后缀树组+rmq)

All knowledge of OI