后缀树的生成——如何做到简单快速(含源码)

         最近两个月一直在看JULY的“结构之法 算法之道”,链接:http://blog.csdn.net/v_JULY_v,收获颇多,在此感谢!其中一篇讲到后缀树的,http://blog.csdn.net/v_july_v/article/details/6897097,讲解了后缀树的结构特征、生成方法和一些用处,看了很久,但还是觉得其中的生成方式比较复杂,要具体实现的话,编码难度有点大,很多编程经验不是很好的都很难正确的编写出来。因此,我希望能得到一个编码难度小,且速度较快的生成方式。终于,在很多次研究各种字符串生成过程后,找到了一个很简单而且速度比较快的生成方式。现描述如下:

    step 1 :
    initialise:  扫描给定字符串的每一个字符,如果该字符和根节点的每一个儿子首字母都不同,则将以此字符为首的整个后缀插入到根节点的儿子中;

    step 2:

  scan_all_suffix:     
             然后从给定字符串的最长后缀到最短后缀依次按trie树的方式在初步建立的树中查找,肯定会出现以下3种情况之一:
             情况一:如果查找成功,则当前后缀处理完成,继续查找下一个后缀;
             情况二:如果对比完一个节点都符合,但在其子节点中找不到下一个分支,查找失败,则直接用当前后缀剩下的字符串建立新节点,并作为当前节点的一个子节点;
             情况三:否者,一定会在一个节点字符串的第 k 个字符处出现查找失败,假设此时已经查找到当前后缀的第m个字符处,这时就可以分割该节点:

1. 以当前查找节点从 k 开始的后缀建立新节点,并作为当前查找结点一个子节点;

2. 当前扫描的后缀从 m 开始的后缀建立新节点,作为其另一个子节点;

3.将当前节点从k开始的后缀去掉;

    当运行完上面两个循环后,一颗后缀树就建立完成了。

    现结合一个实例演示一下流程:

    建立字符串 aabac 的后缀树:

    根据上面的step1,就可以得到下面的树结构:

        后缀树的生成——如何做到简单快速(含源码)_第1张图片

                                                       图一

     先扫描第一个字符 a 插入最长后缀 aabac,再扫描第二个字符 a,因为已经有了以 a 开头的后缀在其中了,故跳过,扫描第三个字符 b ,

没有,插入后缀 bac,第四个字符 a ,跳过,第五个字符 c ,插入后缀 c;

    接着进行第二步:

    scan_all_suffix: 

    扫描第一个后缀 aabac,在图一中的分支 1 的节点找到了改后缀,即上面说的情况一,此后缀处理结束;

    扫描后缀 abac ,如下图:

后缀树的生成——如何做到简单快速(含源码)_第2张图片

                                                                                                           图二

    因为此后缀第一个字符为a 进入分支1,在分支1节点字符串的第二个字符处 查找失败 a!=b,即上面说的情况三,因此将图二中红色及之后的字符串

bac和abac作为当前节点的子节点,并将当前节点第二个a后面的字符串去掉,此步结果如上图右侧;

    接下来的后缀 bac,情况一,忽略;

    后缀 ac:如下图:

后缀树的生成——如何做到简单快速(含源码)_第3张图片

                                                                       图三

    从图二根节点开始查找,先进入根节点的分支1,扫描完该节点 a,发现暂时匹配,需要继续往下查找,但在剩下的两个子节点中找不到以c开始的字符串,

查找失败,也即上面说的情况2,将剩下的字符串 c 作为节点添加到当前节点的子节点中,结果如图三所示;

    最后一个后缀 c 也是情况一,忽略;

    至此aabac的后缀树就建立完成了,每一个后缀都能在其中查找到。

    现将具体实现代码贴下:

    

//mySuffix.h
#ifndef MY_SUFFIX_H
#define MY_SUFFIX_H

#include 
#include 
using namespace std;

typedef struct _TreeNode
{
	string node_text;
	_TreeNode* lChild;
	_TreeNode* sibling;
	_TreeNode()
	{
		lChild=NULL;
		sibling=NULL;
	}
}TreeNode,* pTreeNode;

class mySuffix
{
public:
	mySuffix();
	~mySuffix();
	bool CreateTree(string& constant_str);
	void DestoryTree();
	void show_out();
protected:
	void out(pTreeNode parent,int whitspace);
	void initialise();
	void scan_all_suffix();
	void fix_surffix(pTreeNode parent,string surffix);
	void AddToTree(pTreeNode parent,string contex);
	void CylClear(pTreeNode parent);
private:
	pTreeNode root;
	string constant_str;
};

#endif

/*
	根据后缀树生成步骤经验总结一个新的创建方法:

  	step 1 :
    initialise:  扫描给定字符串的每一个字符,如果该字符和根节点的每一个儿子首字母都不同,
                   则将以此字符为首的整个后缀插入到根节点的儿子中;
    step 2:
    scan_all_suffix:      
             然后从给定字符串的最长后缀到最短后缀依次按trie树的方式在初步建立的树中查找,
             肯定会出现以下3种情况之一:
             情况一:如果查找成功,则当前后缀处理完成,继续查找下一个后缀;
             情况二:如果对比完一个节点都符合,但在其子节点中找不到下一个分支,查找失败,
                          则直接用当前后缀剩下的字符串建立新节点,并作为当前节点的一个子节点;
             情况三:否者,一定会在一个节点字符串的第 k 个字符处出现查找失败,假设此时已经
                          查找到当前后缀的第m个字符处,这时就可以分割该节点,
                          1. 以当前查找节点从 k 开始的后缀建立新节点,并作为当前查找结点一个子节点;
                          2. 当前扫描的后缀从 m 开始的后缀建立新节点,作为其另一个子节点;
                          3.将当前节点从k开始的后缀去掉;

     当运行完上面两个循环后,一颗后缀树就建立完成了。
*/

#include "mySuffix.h"

static int a[128]={0};

mySuffix::mySuffix() : root(NULL),constant_str("") {}

mySuffix::~mySuffix()
{
	if(root) DestoryTree();
	root=NULL;
}

//以contex字符串建立节点并插入到parent的子节点
void mySuffix::AddToTree(pTreeNode parent,string contex)
{
	pTreeNode sub=new TreeNode();
	sub->node_text=contex;
	if( parent->lChild == NULL )
	{
		parent->lChild=sub;
	}
	else
	{
		sub->sibling=parent->lChild;
		parent->lChild=sub;
	}
}

void mySuffix::initialise()
{
	memset(a,0,128*sizeof(int));
	root=new TreeNode();
	root->node_text="root";
	for(int i=0;ilChild;
	if( ! pnode ) return;
	while(pnode)                     //找出可能包含此后缀的分支
	{
		if( surffix[0] == pnode->node_text[0] ) break;
		pnode=pnode->sibling;
	}              
	if( ! pnode ) //情况二:没有分支,直接添加,后缀surffix处理结束
	{
		AddToTree(parent,surffix);
		return;
	}
	//找出了所属的分支
	int i=1; int surffix_len=surffix.size();
	int branch_len=pnode->node_text.size();
	while(1)
	{
		//情况一:发现后缀已经包含在其中了,处理结束
		if( surffix_len < i+1 ) return;
		//一个分直节点已经检查完,用后缀剩下的子串递归检查子节点中下一个可能分支
		if( branch_len < i+1 ) { fix_surffix(pnode,surffix.substr(i)); return; }
		//情况三:分裂当前节点,并插入新节点
		if( surffix[i] != pnode->node_text[i] )
		{
			pTreeNode old_child=pnode->lChild;
			//当前节点字符串中断点及以后的子串作为其子节点
			AddToTree(pnode,pnode->node_text.substr(i));
			//当前节点的原有子节点作为刚添加的节点的子节点
			pnode->lChild->sibling=NULL;
			pnode->lChild->lChild=old_child;
			//当前后缀剩下的子串作为其子节点
			AddToTree(pnode,surffix.substr(i));
			//当前节点去掉其中断点及以后的子串
			pnode->node_text=pnode->node_text.substr(0,i);
			return;
		}
		i++;
	}
}

void mySuffix::scan_all_suffix()
{
	for(int i=1;iroot,constant_str.substr(i));
	}
}

bool mySuffix::CreateTree(string& constant_str)
{
	if( root != NULL || constant_str.size() == 0 ) return false;
	this->constant_str=constant_str;
	initialise();
	scan_all_suffix();
	return true;
}
//释放掉树中各节点占用的空间
void mySuffix::CylClear(pTreeNode parent)
{
	if(parent==NULL) return;
	pTreeNode sibling=parent->sibling;
	pTreeNode temp;
	while( sibling != NULL )
	{
		if(sibling->lChild != NULL) CylClear(sibling->lChild);
		temp=sibling;
		sibling=sibling->sibling;
		delete temp;
	}
	if( parent->lChild != NULL) CylClear(parent->lChild);
	delete parent;
}

void mySuffix::DestoryTree()
{
	CylClear(root);
	root=NULL;
}

void mySuffix::out(pTreeNode parent,int whitspace)
{
	if( parent ==NULL ) return;
	cout <node_text <lChild;
	while( parent != NULL )
	{
		for(int i=0;isibling)
			cout <<"├─";
		else
			cout <<"└─";
		out(parent,whitspace+1);
		parent=parent->sibling;
	}
	
}

void mySuffix::show_out()  //粗略的输出显示树结构
{
	out(this->root,0);
}

#include "mySuffix.h"
#include 

int main()
{
	DWORD start, stop;
	std::string str="cabecabfce";
	
	start = GetTickCount();
	mySuffix mytree;
	if( ! mytree.CreateTree(str) ) cout <<"fail" <

OK.


 
  
 
  
 
  
 
  
 
  
 
  
 
  
 
  
 
  
 
  
 
  
 
  
 
  
 
  
 
  
 
  
 
  
 
  
 
  
 
  
 
  
 
  
 
  
 
  
 
  
 
  
 
 

你可能感兴趣的:(数据结构)