双数组trie构造总结

文章出处

首先需要对词典创建一个DFA(如果对于DFA不熟悉的话,可以看看形式语言和自动机方面的书),构造DFA的过程如下:
对于每一个词a1 a2 … an,依次按该词中每个字的顺序,遍历DFA的状态跳转表,直到遇到该DFA不能接受某个输入时,假设这个字为ai,那么从ai … an将依次建立新的状态以及状态跳转,同时需要对ai进行编码。DFA的实现需要一个数组去保存所有的状态,每个数组元素是一个集合,该集合包含了该状态所能接受的输入以及对应的下一个状态,因而会占用比较大的内存。

其次是把DFA转化成双数组Trie(如果对于双数组Trie不熟悉的话,可以参考http://linux.thai.net/~thep/datrie/datrie.html)转化过程如下:
base值从1开始,双数组Trie的第一个元素的base值为1,check为-1,状态si从0开始,如果双数组Trie的大小不能够容纳状态si的所有下一个状态,那么需要申请更大的内存,之后遍历双数组Trie,找到合适的base值i,设置好Trie.base和Trie.check的值,如果该状态也是一个终止状态,那么需要把Trie.base的最高位设置为1,之后遍历si状态的所有的下一个状态,假设si的输入为ai,ai对应的编码为idx,对应的下一个状态为sj,那么设置Trie[idx].check=i,Trie[idx].base=-1,并把(idx,sj)插入到一个队列中,作为下依次要扩展的状态。所有的状态处理完后,就得到了双数组Trie。为了使双数组Trie尽量占用小的内存,在插入队列时,需要根据sj的所有下一个状态的个数以及空隙的密度进行权衡,比如空隙越大就越靠近队列的前面,使得扩展下一个状态时,尽量能够在sj的状态空隙中来保存。如果插入队列的时间越长,那么构造双数组Trie的时间就越长,目前我采用的是状态空隙比较,即谁的空隙越多,那么就插入到队列的队首,并且如果发现在找base值,如果找了很多次才找到合适的值,那么将适当增加下一次扩展时的base值得初始值,这样就能减少查找base值的时间。

双数组Trie查询词典构造算法由于有些长,暂时未贴出,之后会贴一个带有完整源代码的附件,欢迎大家讨论更好的优化算法。

该双数组Trie查询词典构造算法对Firtex的影响:
由于表示终止状态和以及没有下一个状态的终止状态有些变化,那么Analyzer中需要修改的地方是:

头文件需要修改的地方:
typedef unsigned int int_t;
typedef unsigned short short_t;

struct state
{//state information in double-array trie
int_t base;//base value
int_t check;//check value
int_t handle;//handle for dictionary entry
};

short_t        m_charset[_CHARSET_SIZE];

cpp文件需要修改的地方:

#define FINAL_TAG 0x80000000

nextTokensInternal(CReader* reader,CTokens* pInput)
{
                        int i = 0,nWordLen = 0,nCharLen = 0,j = 0,nStart = 0;
                        size_t nLen;
                        char* sLine = NULL;
                        int_t check,base, nPos;
                        short_t code;
                        int nWordHandle=0;
                        termid_t* tokenBuff = (termid_t*)pInput->getBuffer();
                        CTokenXs<termid_t>* pTokenXs = (CTokenXs<termid_t>*)pInput->asTokensX();
                        int buffbase = 0;
                        char lastwordbuf[50];
                        int lastwordlen = 0;

#define INIT_STATE()        /
                        base=1;                /
                        check=0;        /
                        nWordLen=0;        /
                        nStart=i;        /
                        nWordHandle=0;

                        INIT_STATE();

                        bool bIsEof = false;
                        do
                        {                               
                                if(nWordLen > 0)
                                {
                                        strncpy(lastwordbuf,sLine + nStart - buffbase,nWordLen);
                                        lastwordlen = nWordLen;
                                }

                                sLine = reader->readWithoutCopy(nLen);
                                bIsEof = reader->isEof();
                                buffbase = i;

                                while( (i-buffbase < (int)nLen) ||  bIsEof && ( (nStart-buffbase) < ((int)nLen-1) && nWordLen>0) )
                                //while (i-buffbase < (int)nLen)
                                {
                                        if (i-buffbase >= (int)nLen)  //( (nStart-buffbase) < ((int)nLen-1) && nWordLen>0)
                                        {//Not complete word in the last part                                               

                                                if(!pTokenXs->addTokenX(nWordHandle))
                                                {
                                                        return pInput;
                                                }


                                                i=nStart+nWordLen;

                                                INIT_STATE();
                                                continue;
                                        }

                                        if(i - buffbase < 0)//has back off
                                        {
                                                int c = lastwordlen + i - buffbase;
                                                if ( (lastwordbuf[c] > 0))
                                                {//Single byte character
                                                        code = (unsigned char)lastwordbuf[c];
                                                        nCharLen = 1;//Character Length
                                                }
                                                else//2-byte character
                                                {
                                                        if( (c+1) == lastwordlen )
                                                        {
                                                                code = ((unsigned char)lastwordbuf[c] << 8) | (unsigned char)sLine[0];
                                                        }
                                                        else
                                                        {
                                                                code = ((unsigned char)lastwordbuf[c] << 8) | (unsigned char)lastwordbuf[c+1];//Get code
                                                                nCharLen = 2;//Character Length
                                                        }                                               
                                                }
                                        }
                                        else
                                        {
                                                if ( (sLine[i-buffbase] > 0) || ( (i+1-buffbase == nLen) && reader->isEof()) )
                                                {//Single byte character
                                                        code = (unsigned char)sLine[i-buffbase];
                                                        nCharLen = 1;//Character Length
                                                }
                                                else//2-byte character
                                                {
                                                        if( (i+1-buffbase) == nLen )
                                                        {
                                                                //code = 256*(uint8_t)sLine[i-buffbase];       
                                                                code = ((unsigned char)sLine[i-buffbase]) << 8;       
                                                                strncpy(lastwordbuf,sLine + nStart - buffbase,nWordLen + 1);
                                                                lastwordlen = nWordLen + 1;

                                                                sLine = reader->readWithoutCopy(nLen);
                                                                code |= (unsigned char)sLine[0];
                                                                nCharLen = 2;
                                                                buffbase = i + 1;
                                                        }
                                                        else
                                                        {
                                                                code = ((unsigned char)sLine[i - buffbase] << 8) | (unsigned char)sLine[i+1-buffbase];//Get code
                                                                nCharLen = 2;//Character Length
                                                        }                                               
                                                }

                                        }
                                        i += nCharLen;

                                        if ( !m_charset[code] )
                                        {//Invalid Character
                                                if (nWordLen>0)
                                                {                                                       
                                                        if( nWordHandle != 0 || (j>0 && (tokenBuff[j-1] != 0) ))
                                                        {
                                                                if(!pTokenXs->addTokenX(nWordHandle))
                                                                {
                                                                        return pInput;
                                                                }
                                                                j++;                                                               
                                                        }
                                                        i=nStart+nWordLen;//added 06.5.12
                                                }       
                                                else
                                                {
                                                        if( j>0 && (tokenBuff[j-1] != 0) )
                                                        {
                                                                if(code != ' ')
                                                                {
                                                                        if(!pTokenXs->addTokenX(0))
                                                                        {
                                                                                return pInput;
                                                                        }
                                                                        j++;
                                                                }
                                                               
                                                                //m_pResultID[j++]=0;
                                                        }
                                                }                                               

                                                INIT_STATE();
                                                continue;
                                        }
                                       
                                        nPos = base + m_charset[code];//current position
                                        if (nPos>m_nLowerBound||m_pData[nPos].check!=check)
                                        {//Not exists
                                                if (nWordLen>0)
                                                {//Have a word                                                       
                                                        if( nWordHandle != 0 || (j>0 && (tokenBuff[j-1] != 0) ))
                                                        {
                                                                if(!pTokenXs->addTokenX(nWordHandle))
                                                                {
                                                                        return pInput;
                                                                }
                                                                j++;                                                               
                                                        }
                                                        i = nStart+nWordLen;//Back off
                                                }
                                                else
                                                {//First Character, not exists                                                       
                                                        if( (j>0 && (tokenBuff[j-1] != 0) ))
                                                        {
                                                                if(!pTokenXs->addTokenX(0))
                                                                {
                                                                        return pInput;
                                                                }
                                                                j++;
                                                        }
                                                }
                                                INIT_STATE();
                                                continue;
                                        }               

                                        if (m_pData[nPos].base&FINAL_TAG)
                                        {
                                                check=nPos;

                                                nWordLen = i - nStart;
                                                nWordHandle = m_pData[nPos].handle;//Record Handle

                                                if ( m_pData[nPos].base == -1 )//Leaf
                                                {
                                                        //if (j==0&&lastID!=0||j>0&&m_pResultID[j-1]!=0||nWordHandle!=0)
                                                        if( nWordHandle != 0 || (j>0 && (tokenBuff[j-1] != 0) ))
                                                        {
                                                                if(!pTokenXs->addTokenX(nWordHandle))
                                                                {
                                                                        return pInput;
                                                                }
                                                                j++;
                                                                //m_pResultID[j++]=(unsigned short)nWordHandle;
                                                        }

                                                        INIT_STATE();
                                                        continue;
                                                }
                                                else
                                                {
                                                        base = m_pData[nPos].base & (~FINAL_TAG);
                                                }
                                        }
                                        else
                                        {
                                                base=m_pData[nPos].base;
                                                if (nWordLen==0)//Single Char being a word
                                                {
                                                        nWordLen=nCharLen;
                                                }
                                                check=nPos;
                                        }
                                }//end while

                                if(reader->isEof())
                                {
                                        //if (j==0&&lastID!=0||j>0&&m_pResultID[j-1]!=0||nWordHandle!=0)
                                        if( nWordHandle != 0 )
                                        {
                                                if(!pTokenXs->addTokenX(nWordHandle))
                                                {
                                                        return pInput;
                                                }
                                               
                                                i = nStart+nWordLen;
                                                INIT_STATE();
                                                j++;
                                                continue;
                                               
                                                //m_pResultID[j++]=(unsigned short)nWordHandle;
                                        }                                       
                                }                               
                        }while (!reader->isEof());               

                        return pInput;
}
Load(const tchar *sFilename)
{
                        FILE *fp;
                        fp=_tfopen(sFilename,_T("rb"));
                        if (fp==NULL)
                        {//Open file fail.
                                return false;
                        }
                        fread(m_charset,_CHARSET_SIZE,sizeof(short_t),fp);
                        //Read charset
                        fread(&m_nLowerBound,1,sizeof(int_t),fp);
                        //read lower bound

                        if (m_pData)
                        {
                                free(m_pData);
                        }
                        m_nLength=m_nLowerBound;
                        --m_nLowerBound;
                        m_pData=0;
                        m_pData=(PSTATE)malloc(sizeof(STATE)*m_nLength);
                        fread(m_pData,m_nLength,sizeof(STATE),fp);
                        //read data

                        fclose(fp);
                        return true;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值