上个月写了一个后缀树算法,但是生成树的效率极低,这次重新改写,效率接近线性了,c++实现的,下面面是核心算法:
Node *pTmpNode;
Edge *pEdge, *pTmpEdge;
unsigned int i,edgeNum;
unsigned int matchedPos=0;//匹配到的位置
bool bMatched=false;
m_pOldNode=NULL;
#ifdef Debug
#ifdef add_node
printf("<<<<<<<<<<<<<< '%s' >>>>>>>>>>>>>>>>\n",subData(start,1).c_str());
#endif
#endif
//搜索节点是否包含待加入字符
while(!bMatched)
{
//活动节点:隐含节点为激活节点,或者本身为激活节点
//注意活动节点是非叶节点
//1.匹配整个后缀,即结束节点
//如果是隐含节点
if(m_activeNode.m_bImplicit)
{
matchedPos=m_activeNode.getImplicitPosition();
if(isDataEqual(start,matchedPos))
{
bMatched=true;//退出,因为更短的后缀也肯定是匹配的
}
}
//显示节点
else
{
edgeNum=m_activeNode.m_pNode->getChildNum();
for(i=0;i<edgeNum;i++)
{
matchedPos=m_activeNode.m_pNode->getChildEdge(i)->getStartPos();
if(isDataEqual(start,matchedPos))
{
bMatched=true;
break;
}
}
}
if(bMatched)
{
//更新激活节点
m_activeNode.increaseImplicitNodeOffset(i);//可能显隐互变?
}
//最后字符不匹配
else
{
//2.隐式节点,拆分节点,并添加叶节点
if(m_activeNode.m_bImplicit)
{
//拆分:
pEdge=m_activeNode.getEdge();//上面边
//添加1个节点
pTmpNode=new Node(pEdge);//中间节点
//拆分为2条边
pTmpEdge=new Edge(matchedPos,pEdge->getVirtualEndPos(),pTmpNode,this);//下面边
pEdge->updateEndPosition(matchedPos);//若是叶子节点时,从此摆脱叶子节点
pTmpEdge->setNextNode(pEdge->getNextNode());
pEdge->setNextNode(pTmpNode);
pTmpNode->addEdge(pTmpEdge);
//隐式节点变显示节点,并作为激活节点(有点牵强?)
m_activeNode.setExplicitNode(pTmpNode);
//后缀链接
if(m_pOldNode!=NULL)
m_pOldNode->setNextSuffixPointer(m_activeNode.m_pNode);
m_pOldNode=m_activeNode.m_pNode;
#ifdef Debug
#ifdef add_node
printf("\nsplit:\n %d,%d,%d [%s]\n %d,%d [%s--%s] \n",pEdge->getStartPos(),pEdge->getEndPos(),pTmpEdge->getEndPos(),
subData(pEdge->getStartPos(),pTmpEdge->getEndPos()-pEdge->getStartPos()).c_str(),start,start+1,
subData(pEdge->getStartPos(),pEdge->getEndPos()-pEdge->getStartPos()).c_str(),subData(start,1).c_str());
#endif
#endif
}
//3.显示节点,仅在显示节点下新增叶子节点
//显示节点下新增叶子节点(含上面隐式变显示的节点)
pTmpEdge=new Edge(start,0,m_activeNode.m_pNode,this);//end为0时表示叶子节点
m_activeNode.m_pNode->addEdge(pTmpEdge);
#ifdef Debug
#ifdef add_node
int endPos=0;
if(m_activeNode.m_bImplicit)
endPos=m_activeNode.getEdge()->getEndPos();
else if(m_activeNode.m_pNode!=m_pRoot)
endPos=m_activeNode.m_pNode->getFrontEdge()->getEndPos();
printf("\nnew:\n %d-(%d,%d '%s'--'%s')\n\n",endPos,
start,start+1,subData(endPos-1,1).c_str(),subData(pTmpEdge->getStartPos(),pTmpEdge->charSize()).c_str());
#endif
#endif
//匹配更短的后缀
//判断后缀指针是否存在
if(m_activeNode.m_pNode->hasNextSuffixPointer())
{
m_activeNode.setExplicitNode(m_activeNode.m_pNode->getNextSuffix());
}
//若不存在则手动搜索下一个更短后缀
else
{
//4.当前后缀已经为根节点(非隐含),退出
if(m_activeNode.m_pNode==m_pRoot)//!!!
bMatched=true;
else
{
CharPosition pos;
pTmpNode=m_activeNode.m_pNode;
//要查找的更短后缀
string str;
/*无论如何此时m_activeNode为显示节点
//(开始就是根节点下的)隐含节点
if(m_activeNode.m_bImplicit)//pTmpEdge==NULL
{
pTmpEdge=m_activeNode.getEdge();
str=subData(pTmpEdge->getStartPos(),m_activeNode.m_nImplicitNodeOffsetInEdge);
}*/
while( (pTmpEdge=pTmpNode->getFrontEdge()) != NULL )
{
str.insert(0,subData(pTmpEdge->getStartPos(),pTmpEdge->getEndPos()-pTmpEdge->getStartPos()));
pTmpNode=pTmpEdge->getFrontNode();
if(pTmpNode==m_pRoot)//找到根节点下面的边
{
break;
}
}
str.erase(0,1);
//不连续时会出问题!!!
//str=this->subData(pTmpEdge->getStartPos()+1,matchedPos-(pTmpEdge->getStartPos()+1));
if(str.length()==0)//表示没有更短的后缀了,将根节点作为激活节点
{
m_activeNode.setExplicitNode(m_pRoot);
}
else if(search(str,pTmpNode,pos))//必定能找到,查找到结果后,隐式节点返回值pTmpNode为父节点指针
{
m_activeNode.m_pNode=pTmpNode;
m_activeNode.m_nImplicitNodeEdge=pos.edgeIndex;
m_activeNode.m_bImplicit=(pos.charIndex!=0);
m_activeNode.m_nImplicitNodeOffsetInEdge=pos.charIndex;
}
else
{
printf("################# search error #############\n");
}
}
}
}//end of else
}//end of while