基于三叉Trie树的中文分词 - wyz1989/Segment GitHub Wiki

1.前言

中文分词是自然语言处理基本技术, 一种简单的分词方法是基于词典的分词方法。本文介绍一种基于三叉Trie树(三叉搜索树)的中文分词方法。算法首先针对词典构造一棵三叉搜索树, 其次利用正向最大匹配原理,通过查找三叉搜索树对中文文本进行切分。

2.三叉搜索树

2.1 三叉搜索概念

三叉搜索树为标准Trie树(前缀树)和二叉搜索树的混合体, 它有和标准Trie树差不多的查找速度,但是和二叉搜索树一样只需要相对较小的内存空间。如下图所示:

三叉搜索树

上图为有序词典(as at be by he in is it of on or to),经过平衡化处理后构造出来的三叉搜索树,具体构造方法见下节"三叉搜索树构造"

2.3 三叉搜索树构造

平衡化

三叉搜索树查找效率和树的高度相关,词典输入顺序不同会得到完全不一样的搜索树,因此构造树的时候应该尽量使得树平衡。下面代码片段通过对词典进行排序和输入顺序调整, 来达到使得构造出来的三叉搜索树平衡的目的。

def outputBalancedDict(outputDict, sort_dict, offset, length):
    if length < 1:
        return 
    mid = length / 2
    outputDict.append(sort_dict[mid + offset])
    outputBalancedDict(outputDict, sort_dict, offset, mid)
    outputBalancedDict(outputDict, sort_dict, offset + mid + 1, length - mid -1)


def sortDict(rawDict):
    """
    中文排序
    """
    import locale
    return sorted(rawDict, cmp=locale.strcoll)

树的构造 下面代码片段完成了三叉搜索树构造。TSNode表示的是节点类, TernarySearchTrie为三叉搜索树类, 该类包含两个方法:getNode方法为查询word方法, addWord为将某个词典添加到搜索树中去的方法。

class TSNode(object):
    def __init__(self, splitchar):
        #左边、中间和右边节点
        self.loNode = None
        self.eqNode = None
        self.hiNode = None
        #保存的字符
        self.splitchar = splitchar
        #终止节点保存word
        self.nodeValue = ""

    def toString(self):
        return self.splitchar

class TernarySearchTrie(object):
    def __init__(self, root):
        self.root = root

    def getNode(self, key, startNode):
        """
        查找词key
        """
        if key == "" or key is None:
            return None
        length = len(key)
        if length == 0:
            return None
        currentNode = startNode
        charIndex = 0
        cmpChar = key[charIndex]
        cmpRes = -1
        while 1:
            if currentNode is None:
                return None
            cmpRes = cmp(cmpChar, currentNode.splitchar)
            if cmpRes == 0:
                ####两个字符相等
                charIndex += 1
                if charIndex == length:
                    return currentNode
                else:
                    cmpChar = key[charIndex]
                currentNode = currentNode.eqNode
            elif cmpRes < 0:
                currentNode = currentNode.loNode
            else:
                currentNode = currentNode.hiNode

    def addWord(self, key):
        currentNode = self.root
        charIndex = 0
        cmpRes = -1
        while 1:
            cmpRes = cmp(key[charIndex], currentNode.splitchar)
            if cmpRes == 0:
                charIndex += 1
                if charIndex == len(key):
                    return currentNode
                if currentNode.eqNode is None:
                    currentNode.eqNode = TSNode(key[charIndex])
                currentNode = currentNode.eqNode
            elif cmpRes < 0:
                if currentNode.loNode is None:
                    currentNode.loNode = TSNode(key[charIndex])
                currentNode = currentNode.loNode
            else:
                if currentNode.hiNode is None:
                    currentNode.hiNode = TSNode(key[charIndex])
                currentNode = currentNode.hiNode

3.中文切词

构造好三叉搜索树后我们就可以用搜索树进行中文切词。 切词的思想是通过正向最大长度匹配法,每次从三叉搜索树中查询出最长的匹配作为切词结果, 实现代码如下所示:

class Segment(object):
    def __init__(self, root, text):
        self.tst = root
        ##当前切分位置
        self.offset = 0
        self.text = text

    def splitWordWithMaxMatch(self):
        """
        搜索三叉树并每次返回最长匹配串
        """
        word = ""
        if self.text == "" or self.tst is None:
            return word
        if self.offset >= len(self.text):
            return word
        charIndex = self.offset
        currentNode = self.tst
        while 1:
            if currentNode is None:
                if word == "":
                    word = self.text[self.offset+1]
                    self.offset += 1
                return word
            cmpRes = cmp(self.text[charIndex], currentNode.splitchar)    
            if cmpRes == 0:
                charIndex += 1
                if currentNode.nodeValue != "":
                    word = currentNode.nodeValue
                    self.offset = charIndex
                if charIndex == len(self.text):
                    return word
                currentNode = currentNode.eqNode
            elif cmpRes < 0:
                currentNode = currentNode.loNode
            else:
                currentNode = currentNode.hiNode

4. 实验

输入: [u"大", u"大学", u"大学生", u"活动", u"生活", u"中", u"中心", u"心"]

通过对输入进行排序,平衡化后构造出的三叉搜索树:

                                                                              大                                                                               
                                                                            /  |  \                                                                            
                                                                        中    学    活                                                                         
                                                                         |     |  /  |  \                                                                      
                                                                        心    生 心  动    生                                                                   
                                                                                           |                                                                   
                                                                                          活 

切词输入: u"大学生活动中心" 切词结果: 大学生/活动/中心/

5. github地址

https://github.com/wyz1989/Segment