三叉Trie树实现正向最大长度匹配分词

在一个三叉搜索树(Ternary Search Trie)中,每一个节点包括一个字符,但和数字搜索树不同,三叉搜索树只有三个指针:一个指向左边的树,一个指向右边的树,还有一个向下,指向单词的下一个数据单元。三叉搜索树是二叉搜索树和数字搜索树的混合体。它有和数字搜索树差不多的速度,但是和二叉搜索树一样只需要相对较少的内存空间。
  树的平衡取决于单词的读入顺序。如果按排序后的顺序插入,则生成方式最不平衡。通过选择一个排序后数据单元集合的中间值,并把它作为开始节点,就可以创建一个平衡的三叉树。
  下面用三叉搜索树来构建字典树以及进行最大正向长度匹配分词:

  • TSTNode.java
public final class TSTNode {
    /**节点的值data可以存储词原文和词性、词频等相关的信息*/
    public String data=null;
    //左中右节点
    protected TSTNode lNode;
    protected TSTNode mNode;
    protected TSTNode rNode;
    
    protected char splitchar;//本节点表示的字符
    
    protected TSTNode(char splitchar){
        this.splitchar=splitchar;
    }
    public String toString(){
        return "splitchar:"+splitchar;
    }
    /**
     * 查找的过程是:输入一个词,返回这个词对应的TSTNode对象,如果该词不在词典中,则返回空。
     * 查找词典的过程中,从树的根节点匹配Key,从前往后匹配Key*/
    protected TSTNode getNode(String key,TSTNode startNode){
        if(null==key){
            return null;
        }
        int len=key.length();
        if(len==0)
            return null;
        TSTNode currentNode=startNode;//匹配过程中当前节点的位置
        int charIndex=0;
        char cmpChar=key.charAt(charIndex);
        int charComp;
        while(true){
            if(null==currentNode){
                return null;
            }
            charComp=cmpChar-currentNode.splitchar;
            if(charComp==0){//equal
                charIndex++;
                if(charIndex==len){//找到                 
                    return currentNode;
                }
                else
                    cmpChar=key.charAt(charIndex);

                currentNode=currentNode.mNode;
            }else if(charComp<0){//小于
                currentNode=currentNode.lNode;
            }else {
                currentNode=currentNode.rNode;
            }
        }
    }
    
    /**三叉树的创建过程,就是在Trie树上创建和单词对应的节点
     * 下面方法实现的是向词典树中加入一个单词的过程*/
     TSTNode addWord(String key,TSTNode root){
        TSTNode currentNode=root;//从树的根节点开始查找
        int charIndex=0;//从词的开头匹配
        while(true){
            //比较词的当前字符与节点的当前字符
            int charComp=key.charAt(charIndex)-currentNode.splitchar;
            if(charComp==0){
                charIndex++;
                if(charIndex==key.length()){
                  currentNode.data=key;//将当前的词存到最后一个节点的data中
                    return currentNode;
                }
                //如果不存在直接后继节点
                if(currentNode.mNode==null){
                    currentNode.mNode=new TSTNode(key.charAt(charIndex));
                }
                currentNode=currentNode.mNode;
            }else if (charComp<0) {
                if(currentNode.lNode==null){
                    currentNode.lNode=new TSTNode(key.charAt(charIndex));
                }
                currentNode=currentNode.lNode;
            }else {
                if(currentNode.rNode==null){
                    currentNode.rNode=new TSTNode(key.charAt(charIndex));
                }
                currentNode=currentNode.rNode;
            }
        }
    }
    
     /**Trie树搜索最长匹配单词的方法
      * @param key 待匹配字符串
      * @param offset 匹配开始位置*/
     public String matchLong(String key,int offset,TSTNode root){
         String ret=null;
         if(key==null||root==null||"".equals(key)){
             return ret;
         }
         TSTNode currentNode=root;
         int charIndex=offset;
         while(true){
             if(currentNode==null){
                 return ret;
             }
             int charComp=key.charAt(charIndex)-currentNode.splitchar;
             if(charComp==0){
                 charIndex++;
                 if(currentNode.data!=null){
                     ret=currentNode.data;//候选最长匹配词
                 }
                 if(charIndex==key.length()){
                     return ret;
                 }
                 currentNode=currentNode.mNode;
             }else if (charComp<0) {
                currentNode=currentNode.lNode;
            }else {
                currentNode=currentNode.rNode;
            }
         }
     }
     /**正向最大长度分词*/
     public void wordSegment(String sentence,TSTNode root){
         int senLen=sentence.length();
         int i=0;
         while(i<senLen){
             String word=root.matchLong(sentence, i, root);
             if(word!=null){
                 //下次匹配点在这个词之后
                 i+=word.length();
                 //如果词在词典中,则就直接打印出来
                 System.out.print(word+" ");
             }
             else{
                 //如果在词典中没找到,则按单字切分
                 word=sentence.substring(i, i+1);
                 //打印一个字
                 System.out.print(word);
                 i++;//下次匹配点在这个字符之后
             }
         }
     }
     
}
  • TSNTreeTest.java
    例如,Trie树的字典中包含如下词语:

大 大学 大学生 活动 生活 中 中心 心

(下面直接按照上面字典的顺序添加,所以得到的可能不是平衡Trie树)

public class TSNTreeTest {

    public static void main(String[] args) {
        TSTNode root=new TSTNode(' ');
        root.addWord("大", root);
        root.addWord("大学", root);
        root.addWord("大学生", root);
        root.addWord("活动", root);
        root.addWord("生活", root);
        root.addWord("中", root);
        root.addWord("中心", root);
        root.addWord("心", root);
        
        String sentence="大学生活动中心";
        String ret=root.matchLong(sentence, 0, root);
        System.out.println(sentence+" match:"+ret);
        root.wordSegment(sentence, root);
    }
}

得到的结果是:

第一行是输入“大学生活动中心”时,返回的最长匹配词
第二行是根据正向最长匹配分词的结果

参考资料:
[1] Trie树和Ternary Search树的学习总结:
  http://www.cnblogs.com/rush/archive/2012/12/30/2839996.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 224,509评论 6 522
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 96,093评论 3 402
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 171,632评论 0 366
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 60,848评论 1 300
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 69,855评论 6 399
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 53,353评论 1 314
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 41,738评论 3 428
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 40,706评论 0 279
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 47,240评论 1 324
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 39,268评论 3 345
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 41,398评论 1 354
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 37,012评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 42,709评论 3 337
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 33,178评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 34,312评论 1 275
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 49,932评论 3 381
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 46,464评论 2 365

推荐阅读更多精彩内容

  • 转载请注明:终小南 » 中文分词算法总结 什么是中文分词众所周知,英文是以 词为单位的,词和词之间是靠空格隔开,而...
    kirai阅读 9,850评论 3 24
  • 常用概念: 自然语言处理(NLP) 数据挖掘 推荐算法 用户画像 知识图谱 信息检索 文本分类 常用技术: 词级别...
    御风之星阅读 9,209评论 1 25
  • (本文转自百度搜索第一个CSDN博客) 一、知识简介 Trie 的强大之处就在于它的时间复杂度。它的插入和查询时间...
    Alan66阅读 834评论 0 0
  • 花叶生生两不见 相念相惜永相失 三生石畔的回眸 才注定了带伤的轮回
    唯意soleone阅读 107评论 0 0
  • 参与伙伴: U的工具箱 聆听评估工具日志练习 15:00 开始 根据参加的同伴讨论,我们计划先做同理心漫步,然后是...
    wxLite阅读 987评论 0 49