http://www.hankcs.com/program/java/tire-tree-participle.html

最近在看Ansj中文分词的源码,以前没有涉足过这个领域,所以需要做一些笔记。

Trie树

首先是Ansj分词最基本的数据结构——Trie树。Trie树也称字典树,能在常数时间O(len)内实现插入和查询操作,是一种以空间换取时间的数据结构,广泛用于词频统计和输入统计领域。

Ansj作者ansjsun为此数据结构专门开了一个项目,clone下来之后可以用作者提供的一个demo进行测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package com.hankcs;
 
import love.cq.domain.Forest;
import love.cq.library.Library;
import love.cq.splitWord.GetWord;
 
import java.io.BufferedReader;
import java.io.StringReader;
 
/**
 * @author hankcs
 */
public class Main
{
    public static void main(String[] args) throws Exception
    {
/**
 * 词典的构造.一行一个词后面是参数.可以从文件读取.可以是read流.
 */
        String dic =
                "中国\t1\tzg\n" +
                "人名\t2\n" +
                "中国人民\t4\n" +
                "人民\t3\n" +
                "孙健\t5\n" +
                "CSDN\t6\n" +
                "java\t7\n" +
                "java学习\t10\n";
        Forest forest = Library.makeForest(new BufferedReader(new StringReader(dic)));
 
        /**
         * 删除一个单词
         */
        Library.removeWord(forest, "中国");
        /**
         * 增加一个新词
         */
        Library.insertWord(forest, "中国人");
        String content = "中国人名识别是中国人民的一个骄傲.孙健人民在CSDN中学到了很多最早iteye是java学习笔记叫javaeye但是java123只是一部分";
        GetWord udg = forest.getWord(content);
 
        String temp = null;
        while ((temp = udg.getFrontWords()) != null)
            System.out.println(temp + "\t\t" + udg.getParam(1) + "\t\t" + udg.getParam(2));
    }
}

输出:

1
2
3
4
5
6
7
中国人     null        null
中国人民        null        null
孙健      null        null
人民      null        null
CSDN        null        null
java学习      null        null
java        null        null

这段demo的目的是利用一个小词典对后面一句话进行分词,词典被用来构造了一颗Trie树,也就是代码中的forest。

词典每一行第一列是单词,之后的几列都是param(属性)。

在tree_split中,一棵Trie树有四种不同的节点:

  • 根节点,上图的绿色节点。被称为Forest,没有实际含义,也不含属性。

  • 起始节点,上图的蓝色节点。是一个单词的开头第一个字,不含属性。

  • 中继节点,上图的黄色节点。可能是一个单词的结尾,含属性;也可能是另一个更长的单词的中间某个字,不含属性。

  • 结束节点,上图的红色节点。是一个单词的结尾,含属性。

根节点使用Forest描述,而其它三种节点统一使用Branch描述,并用status = 1 2 3 来区分,它们有如下的类图关系:

Root在构造的时候开了212个空槽以供放置子节点,每个汉字和其他字符都落在这个范围内。每次查找直接用汉字作为下标即可定位,Branch则使用动态数组分配内存,使用二分查找定位,这是Trie树的高速秘诀。Trie树的查询和插入都是类似的方法:从根节点开始沿着词语的开头字符走到结尾字符。在这里除了完成基本的维护操作,还需维护Branch的status。

删除操作比较讨巧,统一将要删除的单词最后一个字对应的节点设为“起始节点”,那么它就不能构成这个词了。

词典分词

词典分词是一种实现简便、速度快但是错误率高的分词方式。用Trie树词典分词就是按照句子的字符顺序从root往下走,每走到一个结束节点则分出一个词。中途遇到的中继节点统统忽略,这种方式也称“最长匹配”,是一种很武断的方式。比如下面这个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.hankcs;
 
import love.cq.domain.Forest;
import love.cq.library.Library;
import love.cq.splitWord.GetWord;
 
import java.io.BufferedReader;
import java.io.StringReader;
 
/**
 * @author hankcs
 */
public class Main
{
    public static void main(String[] args) throws Exception
    {
/**
 * 词典的构造.一行一个词后面是参数.可以从文件读取.可以是read流.
 */
        String dic =
                "商品\t1\tzg\n" +
                "和服\t2\n" +
                "服务\t4\n" ;
        Forest forest = Library.makeForest(new BufferedReader(new StringReader(dic)));
 
        String content = "商品和服务";
        GetWord udg = forest.getWord(content);
 
        String temp = null;
        while ((temp = udg.getFrontWords()) != null)
            System.out.println(temp + "\t\t" + udg.getParam(1) + "\t\t" + udg.getParam(2));
    }
}

输出:

1
2
商品      zg      null
和服      null        null

很明显,效果不好。

要想提高分词效果,就必须引入条件概率(隐马尔可夫模型),这就是Ansj分词的使命吧。

Trie树分词的更多相关文章

  1. Ansj分词双数组Trie树实现与arrays.dic词典格式

    http://www.hankcs.com/nlp/ansj-word-pairs-array-tire-tree-achieved-with-arrays-dic-dictionary-format ...

  2. [转]双数组TRIE树原理

    原文名称: An Efficient Digital Search Algorithm by Using a Double-Array Structure 作者: JUN-ICHI AOE 译文: 使 ...

  3. 双数组Trie树 (Double-array Trie) 及其应用

    双数组Trie树(Double-array Trie, DAT)是由三个日本人提出的一种Trie树的高效实现 [1],兼顾了查询效率与空间存储.Ansj便是用DAT(虽然作者宣称是三数组Trie树,但 ...

  4. trie树信息抽取之中文数字抽取

    这一章讲一下利用trie树对中文数字抽取的算法.trie树是一个非常有用的数据结构,可以应用于大部分文本信息抽取/转换之中,后续会开一个系列,对我在实践中摸索出来的各种抽取算法讲开来.比如中文时间抽取 ...

  5. 双数组trie树的基本构造及简单优化

    一 基本构造 Trie树是搜索树的一种,来自英文单词"Retrieval"的简写,可以建立有效的数据检索组织结构,是中文匹配分词算法中词典的一种常见实现.它本质上是一个确定的有限状 ...

  6. 从Trie树到双数组Trie树

    Trie树 原理 又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种.它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,能在常数时间O(len)内实现插入和查 ...

  7. python Trie树和双数组TRIE树的实现. 拥有3个功能:插入,删除,给前缀智能找到所有能匹配的单词

    #coding=utf- #字典嵌套牛逼,别人写的,这样每一层非常多的东西,搜索就快了,树高26.所以整体搜索一个不关多大的单词表 #还是O(). ''' Python 字典 setdefault() ...

  8. 双数组Trie树(DoubleArrayTrie)Java实现

    http://www.hankcs.com/program/java/%E5%8F%8C%E6%95%B0%E7%BB%84trie%E6%A0%91doublearraytriejava%E5%AE ...

  9. 数据结构 | 30行代码,手把手带你实现Trie树

    本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是算法和数据结构专题的第28篇文章,我们一起来聊聊一个经典的字符串处理数据结构--Trie. 在之前的4篇文章当中我们介绍了关于博弈论的 ...

随机推荐

  1. NOIP 2013 转圈游戏

    [题目描述] n个小伙伴(编号从 0 到 n−1)围坐一圈玩游戏.按照顺时针方向给 n 个位置编号,从 0 到 n−1.最初,第 0 号小伙伴在第 0 号位置,第 1 号小伙伴在第 1 号位置,……, ...

  2. 洛谷P4141消失之物

    题目描述 ftiasch 有 N 个物品, 体积分别是 W1, W2, …, WN. 由于她的疏忽, 第 i 个物品丢失了. “要使用剩下的 N – 1 物品装满容积为 x 的背包,有几种方法呢?” ...

  3. Codeforces Round #371 (Div. 1) D. Animals and Puzzle 二维倍增

    D. Animals and Puzzle 题目连接: http://codeforces.com/contest/713/problem/D Description Owl Sonya gave a ...

  4. 如何使用windows云服务器搭建IIs、windows服务

    如何使用windows云服务器搭建IIs.windows服务,以下针对腾讯云服务器进行说明 1.购买云服务器之后,第1步需要设置的是,找到重装系统.重置密码等处. 2.设置安全组,设置完安全组之后才能 ...

  5. Code Fragment-UI加载策略之-可视者优先加载

    通常情况 通常程序的UI不太复杂,我们会直接加载这些UI信息 复杂的UI 加载的元素就相对多一些. 加载的数据相对多. 因为UI元素和数据元素都比较多,加载的时间相对多. 可视者优先加载 不是默认的加 ...

  6. 一共81个,开源大数据处理工具汇总(下),包括日志收集系统/集群管理/RPC等

    作者:大数据女神-诺蓝(微信公号:dashujunvshen).本文是36大数据专稿,转载必须标明来源36大数据. 接上一部分:一共81个,开源大数据处理工具汇总(上),第二部分主要收集整理的内容主要 ...

  7. Python:Hello World级别的SimpleDb

    背景 几乎所有的动态语言都支持成员的动态解析,一般的在解析不到成员的时候会给出一个hook点让你自定义一些有意思的实现..Net4之后增加了对动态类型的支持,在动态类型上就有这种机制. 模拟Simpl ...

  8. ASP.NET MVC:如何提供 Controller 继承体系使用的 ModelBinder?

    背景 Mvc 提供了一种可扩展的模型绑定机制,具体来说就是:将客户端传递的参数按照一定的策略绑定到 action 的参数上,这带来的直接好处就是让 action 的参数支持强类型.一般来说我们有如下方 ...

  9. localhost与127.0.0.1及本机ip的区别

    很多人会接触到这个ip地址127.0.0.1.也许你会问127.0.0.1是什么地址?其实127.0.0.1是一个回送地址,指本地机,一般用来测试使用.大家常用来ping 127.0.0.1来看本地i ...

  10. Python中的文件IO操作(读写文件、追加文件)

    Python中文件的读写包含三个步骤:打开文件,读/写文件,关闭文件. 文件打开之后必须关闭,因为在磁盘上读写文件的功能是由操作系统提供的,文件作为对象,被打开后会占用操作系统的资源,而操作系统在同一 ...