trie树信息抽取之中文数字抽取
这一章讲一下利用trie树对中文数字抽取的算法。trie树是一个非常有用的数据结构,可以应用于大部分文本信息抽取/转换之中,后续会开一个系列,对我在实践中摸索出来的各种抽取算法讲开来。比如中文时间抽取,地址抽取等。
Trie树
trie树又称为前缀树,索引树,字典树。用来对字符串进行索引,每个节点存储一个字符,每个叶子节点代表一个字符串,即从根到它的路径上所有字符的序列。
这个结构有什么优点呢?可以快速的匹配一个目标字符串中存在的单词。换句话说,我有一个字典,是单词的集合,我把字典中所有的单词存储在trie树中,然后来一句话,我们可以利用trie树(字典树)快速的匹配出这句话中出现的字典中的所有单词,以及每个单词的位置。快速的意思是,复杂度近似于$O(L)$,其中$L$为这句话的长度,其速度不因字典的增大而降低。具体的匹配算法可以问百爷和姑爷,哦不,谷爷。
很多采用字典进行分词的分词器,都是用trie树进行分词的,比如IK分词。
利用trie树进行信息抽取,还有一个优点就是。逻辑非常清晰,非常方便扩展,大大降低了代码的复杂度和代码量,甚至后续扩展功能可以完全不用改动代码只需修改配置文件(字典)即可。
本章和后续章节中,我们用到的是扩展了的trie树,其叶子节点存储了相应数据,而且实现了无交叉的最大长度抽取器(分词器)以应对具体的业务。
中文数字抽取/转换
所谓中文数字的抽取/转换,就是下表:
| 原字符串 | 抽取/转换后 |
| 一千二百零8吨大米和三十袋盐 | 1208吨大米和30袋盐 |
| 第一二五分队 | 第125分队 |
| 二百1十五个苹果 | 215个苹果 |
| 。。。 | 。。。 |
名虽为中文数字抽取,其实对所有其它语言也完全适用,所做的只是修改配置文件即可。
下面介绍算法主要步骤:
1、trie树内容。
首先,我们要对汉字数字【零~十】这十一个字进行匹配,所以把这11个字插入trie树中,节点存储对应的数字;
然后,我们要对阿拉伯数字【0~9】这十个字进行匹配,所以把这10个字插入trie树,节点存储对应的数字;
以上是数字插入,共插入21个字。下面是带单位插入。
我们要对汉字的单位【十,百,千,万,亿,...】进行匹配,所以把这这5个单位的数字插入trie树中,对应节点值分别为【10,100,1000,10000,100000000,...】。
从1到9,组合上述的数字和这5个单位,共$(9+9)\times 5=120$个字:
| 插入词 | 节点存储 |
| 一千~九千 | 1000~9000 |
| 1千~9千 | 1000~9000 |
| 一万~九万 | 10000~90000 |
| 。。。 | 。。。 |
当然,若要对记账的大写字进行匹配,同样方法插入即可。
以上总共插入141个词以及附带数据,插入完毕。插入代码示例如下:
String[] hanzi = new String[] { "零", "一", "二", "三", "四", "五", "六", "七",
"八", "九", "十" };
Map<String, Integer> unitMap = new HashMap<String, Integer>() {
private static final long serialVersionUID = 1738199399754758349L;
{
put("十", 10);
put("千", 1000);
put("百", 100);
put("万", 10000);
}
};
for (Entry<String, Integer> entry : unitMap.entrySet()) {
for (int i = 1; i < 10; i++) {
Integer num = i * entry.getValue();
numTrie.fillWithData(hanzi[i] + entry.getKey(), num);
numTrie.fillWithData(i + entry.getKey(), num);
}
}
for (int i = 0; i < hanzi.length; i++) {
numTrie.fillWithData(hanzi[i], i);
}
上面代码只是示例,实际中可以把插入内容配置在文件中,启动时读取配置文件即可。以后扩展可以直接修改配置文件。
2、数字抽取/转换
第一步生成好了字典,打好了内功,这一步开始施展,拉出去遛一下,实现数字的抽取与转换。
现在以【一千二百零8吨大米】为例说明,首先利用上述字典匹配出的单词为:
| 单词 | 绑定数据 | 位置 |
| 一千 | 1000 | 0~2 |
| 二百 | 200 | 2~4 |
| 零 | 0 | 4~5 |
| 8 | 8 | 5~6 |
首先要区分数字边界,因为一句话中可能含有多个数字串。现在筛选来的都是兄弟,哦不,数字。这5个单词的位置首尾相连,可以融合成一个数字。
下面要做的就是,把绑定的数字全部加起来即可。$1000+200+0+8=1208$。OK,一个数字被我们抽取出来了,附带在原句子中的位置为0~6。
上面只是抽取出来的简单例子。实际中我们可能还要处理其它的格式,比如例子中的【第一二五分队】,我们所要出来的是【第125分队】,而按上述做法,出来的却是【第8分队】,非我所求。分析这类句子,我们发现这个数字字符串的每个元素都是个位(<10),若出现这种情况,我们不直接相加,而是直接显示其绑定数据的文本,然后拼接起来即可。
到此为止,已经把核心思想将清楚了。同志们会发现这个算法可扩展性非常强,稍微修改一下就可满足非常复杂的需求。下面附上相关的示例代码。
单词匹配:
Segmenter<Integer> segmenter = new Segmenter<Integer>(numTrie);
TranslateProcessor processor=new TranslateProcessor();
segmenter.segment(str, processor);
return processor.getResult();
str为待抽取的句子,segmenter为分词器,抽取出来无交叉的最大长度单词,每抽取出来一个单词,调取TranslateProcessor进行处理,处理完毕后processor.getResult()可以获取转换后的结果。下面是TranslateProcessor的具体实现,算法的核心在这里:
private static class TranslateProcessor implements LexemeProcessor<Integer> {
String strResult = "";
int numEndOffset = -1;
int currentNum = 0;
List<Integer> intResult=new ArrayList<Integer>();
public String getResult() {
return strResult;
}
@Override
public void processLexeme(Lexeme<Integer> lex) {
// 没有匹配到数字
if (lex.getNodeData() == null) {
// 若前面已经抽取出数字,数字加入结果,清空状态
if (numEndOffset > -1) {
strResult += currentNum;
intResult.add(currentNum);
numEndOffset = -1;
currentNum = 0;
}
strResult += lex.getLexemeText();
return;
}
// 数字匹配,并且紧邻,则进行合并,若上一个是个位数,不合并
if (lex.getBeginPosition() == numEndOffset) {
if (currentNum < 10) {
strResult += currentNum;
currentNum = 0;
}
currentNum += lex.getNodeData();
numEndOffset = lex.getEndPosition();
return;
}
currentNum = lex.getNodeData();
numEndOffset = lex.getEndPosition();
}
}
整个算法代码加起来不到100行。
trie树信息抽取之中文数字抽取的更多相关文章
- 数据结构 | 30行代码,手把手带你实现Trie树
本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是算法和数据结构专题的第28篇文章,我们一起来聊聊一个经典的字符串处理数据结构--Trie. 在之前的4篇文章当中我们介绍了关于博弈论的 ...
- 笔试算法题(39):Trie树(Trie Tree or Prefix Tree)
议题:TRIE树 (Trie Tree or Prefix Tree): 分析: 又称字典树或者前缀树,一种用于快速检索的多叉树结构:英文字母的Trie树为26叉树,数字的Trie树为10叉树:All ...
- 剑指Offer——Trie树(字典树)
剑指Offer--Trie树(字典树) Trie树 Trie树,即字典树,又称单词查找树或键树,是一种树形结构,是一种的单词.对于每一个单词,我们要判断他出没出现过,如果出现了,求第一次出现在第几个位 ...
- python Trie树和双数组TRIE树的实现. 拥有3个功能:插入,删除,给前缀智能找到所有能匹配的单词
#coding=utf- #字典嵌套牛逼,别人写的,这样每一层非常多的东西,搜索就快了,树高26.所以整体搜索一个不关多大的单词表 #还是O(). ''' Python 字典 setdefault() ...
- 双数组Trie树(DoubleArrayTrie)Java实现
http://www.hankcs.com/program/java/%E5%8F%8C%E6%95%B0%E7%BB%84trie%E6%A0%91doublearraytriejava%E5%AE ...
- Trie 树——搜索关键词提示
当你在搜索引擎中输入想要搜索的一部分内容时,搜索引擎就会自动弹出下拉框,里面是各种关键词提示,这个功能是怎么实现的呢?其实底层最基本的就是 Trie 树这种数据结构. 1. 什么是 "Tri ...
- 字典树(Trie树)的实现及应用
>>字典树的概念 Trie树,又称字典树,单词查找树或者前缀树,是一种用于快速检索的多叉树结构,如英文字母的字典树是一个26叉树,数字的字典树是一个10叉树.与二叉查找树不同,Trie树的 ...
- [转]数据结构之Trie树
1. 概述 Trie树,又称字典树,单词查找树或者前缀树,是一种用于快速检索的多叉树结构,如英文字母的字典树是一个26叉树,数字的字典树是一个10叉树. Trie一词来自retrieve,发音为/tr ...
- 双数组Trie树 (Double-array Trie) 及其应用
双数组Trie树(Double-array Trie, DAT)是由三个日本人提出的一种Trie树的高效实现 [1],兼顾了查询效率与空间存储.Ansj便是用DAT(虽然作者宣称是三数组Trie树,但 ...
随机推荐
- 设计模式之装饰者模式(Decorator Pattern)
一.什么是装饰者模式? 装饰者模式能够完美实现“对修改关闭,对扩展开放”的原则,也就是说我们可以在不修改被装饰者的前提下,扩展被装饰者的功能. 再来看看我们的文件操作代码: 1 InputStream ...
- TransactionScope IsolationLevel 事务隔离级别
事务有四个特性 第一原子性,事务具有独立的不能被拆分的,不能只做部分,事务的性质是要么全做,要么都不做. 第二统一性,在事务执行之前和事务执行之后的数据是一致. 第三隔离性,事务是独立的,开发者不能查 ...
- QT、QTE、qtopia区别
QT.QTE.qtopia区别 Qt的授权是分为两条线,商业版和开源版.如果使用商业版的Qt,那么开发出的程序可以是私有的和商业的:如果使用的是开源版的Qt,由于其使用的是GPL协议,那么可发出的程序 ...
- WordPress /wp-admin/users.php畸形s参数路径泄漏漏洞
漏洞版本: WordPress 2.7.x WordPress 2.8.x WordPress 2.9.x WordPress 3.0.x WordPress 3.1.x WordPress 3.2. ...
- Linux 多线程调试(内存占用、死循环、CPU占用率高……)
文章出处:http://www.cnblogs.com/cy568searchx/archive/2013/10/28/3391790.html 你的软件在某个时刻停止服务,CPU占用达到100%+, ...
- 图论(网络流):[SDOI2010] 星际竞速
Description 10 年一度的银河系赛车大赛又要开始了.作为全银河最盛大的活动之一,夺得这个项目的冠军无疑是很多人的梦想,来自杰森座 α星的悠悠也是其中之一. 赛车大赛的赛场由 N 颗行星和M ...
- FFT(快速傅里叶变换):HDU 4609 3-idiots
3-idiots Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total S ...
- vijos P1243 生产产品(单调队列+DP)
P1243生产产品 描述 在经过一段时间的经营后,dd_engi的OI商店不满足于从别的供货商那里购买产 品放上货架,而要开始自己生产产品了!产品的生产需要M个步骤,每一个步骤都可以在N台机器 ...
- hdu 4602 Partition 数学(组合-隔板法)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4602 我们可以特判出n<= k的情况. 对于1<= k<n,我们可以等效为n个点排成 ...
- wireshark的使用
1. 过滤 比如输入www.baidu.com 使用域名过滤条件为 跟踪的过程是: 105和106是dns查询,108和115是dns回应,为什么会有两条,我也不是和清楚 展开106 可以它使用的是U ...