搜索引擎中长串匹配及聚类算法:后缀树算法

 上个月写了一个后缀树算法,但是生成树的效率极低,这次重新改写,效率接近线性了,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



 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值