双数组字典树(Double Array Trie)
参考文献
3.论文《基于双数组Trie树算法的字典改进和实现》
DAT的基本内容介绍这里就不展开说了,从Trie过来的同学应该比较熟悉,Trie对内存的消耗比较大,DAT正是为了优化该问题而提出。此文重点说一下如何去理解DAT的base数组和check数组,希望能给诸位些帮助,DAT中定义base数组、check数组满足的条件为:
base[s] + c = t
check[t] = s
这里s指转移前的状态,c指字符的编码,t指转移后的状态,下面逐个理解这两个公式表示的逻辑。
1. base[s] + c = t
理解这个式子时,不能单纯的从数组取值上去理解,比如直接将s当成base数组的下标,因为 s 和 t 都是状态,而 c 是字符编码,此时就会疑惑,为啥等式两边输出类型都不一致呢?这到底是怎么个计算关系?如果有类似的疑惑,那么可以先摒弃这种的想法。
base数组维护的是Trie树上节点的信息,这个公式想表达的意思是:状态 s (也就是一个节点,Trie树上每个节点都表示一个状态,不理解的可以先了解一下状态机的概念)接收一个字符 c 后,就得到状态 t ,而并不是base数组中下标为 s 处的取值加上 c值 等于 t 值,所以说不能从仅仅从数值计算上理解这个公式。那么这个公式是如何用在DAT的创建中的呢?我们从如下几个问题入手,来了解这个公式的作用。
1.1 s、t 具体是什么意思?
s、t 表示DAT上的一种状态、base数组中的元素,实际上就是Trie上的一个个节点,这个节点上包含很多信息,创建过Trie树的同学知道,Trie树节点上一般包含:属性值value(可以用来记录词性)、叶子节点标记flag(是否为词语的未字符)、子节点数组(用来存储子节点的信息)等等
1.2 base数组中存储的是什么?
base数组维护的是各个状态的信息,即数组中存储的是各节点的信息,但是具体内容与1.1节中所说的Trie节点信息不一致,以参考文献1中实现为例,每个状态下包含的信息包括
transferRatio:计算子节点在base数组中下标时的转移基数,为了解决节点存储位置冲突而引入的,初始值为0
isLeaf:节点是否为词语的叶子节点。该内容可选,也可以用其它方式表示叶子节点
label:节点存储的字符,可以理解为该节点是通过插入哪个字符得到的。该内容可选,也可以不要
value:如果该节点为叶子节点,那么其对应的词语在词典中序号。该内容为可选,也可以不要
1.3 base数组的下标表示什么意思
base数组的下标是基于字符的编码做加减运算得到的数值。参照1.2节中的例子,创建DAT存储词语“中华”,先创建一个base数组,
TrieNode base[10] //假设存储10个节点
令根节点root = new TrieNode ,很自然的将根节点root存入base[0]。令 root. transferRatio=1 (这里设为1,也可以设个其他值,初始值随便,保证base数组不溢出即可)
接着插入字符“中”,假如采用unicode编码,那么“中”的码值为20013,那么存储 字符“中”的节点在base数组中的下标就为
0 + base[0].transferRatio + unicode("中")=0+1+20013=20014
令 base[20014].transferRatio=1 (这里设为1主要是为了和初始时的0区分开,表示base[20014]这个位置已经有节点占据了)
然后插入字符“华”,unicode('华')=21326,因为是节点“中”接收字符“华”,存储 字符“华”的节点在base数组中的下标就为
20014+ base[20014].transferRatio + unicode('华')=20014+0+21326=41340,
令base[41340].transferRatio=1 ,原因同上。
1.4 c值怎么计算
c值得计算除了1.3中说的计算字符得unicode值,你还可以计算字符hash值得到,只要保证每个字符(中文、英文、日文等等文字的字符)有一个唯一且确定得编码值即可。
通过以上几个问题的说明可知,DAT中每个状态的下标是唯一的(解决位置冲突之后),可以用base数组的下标表示状态,通过索引base数组的下标即可得到各个状态的信息。此时,我们再来从数值计算的角度来理解这个公式,即状态 s 的下标(还要加上偏移transferRatio)加上字符c的编码值,等于状态 t (由状态s 接收字符c 得到)的下标。
2. check[t] = s
理解这个等式,先要从DAT的查询逻辑说起。在Trie中,我们是怎么判断词语存在的?从根节点开始,依次查询词语中的每个字符,若各个字符均存在于当前节点的子节点中,则表明该词语存在。如果在DAT上也按照这个逻辑来判断词语是否存在,则查询过程是这样的:还是以1.3节为例,查询词语 “中华” 是否存在。从根节点开始,首先查询 “中”字,
0 + base[0].transferRatio + unicode("中")=0+1+20013=20014
若base[20014].transferRatio ≠ 0 ,则表明字符“中”存在。(判断base数组中一个位置上是否有数据可以采用很多方式,这里采用参考文献1中的实现方式,即判断TrieNode.transferRatio是否非0)
接着查询字符“华”,
20014+ base[20014].transferRatio + unicode('华')=20014+0+21326=41340
若base[20014].transferRatio ≠ 0,则表明“华”存在。那么这个逻辑在DAT上是否可靠呢?答案是不可靠,因为在DAT上节点转移是通过在base数组上索引字符unicode码值进行的,这就会存在以下的情况
unicode(A) ≠ unicode(B)
unicode(A)+unicode(C) = unicode(B)+unicode(D)
其中 A、B、C、D指unicode编码规范中收录的某个字符,此种情况下的查询逻辑就会出错,所以在DAT中引入了check数组,在check数组中保存的是当前状态的父状态(即当前节点的父节点),还是以1.3节中词语“中华”为例,以base数组的下标表示状态,字符“中”所在节点的父节点为根节点,所以check[20014]=0;字符“华”所在节点的父节点下标为20014,即check[41340]=20014,这样可以确定当前节点的父节点是哪一个,从而解决 unicode(A)+unicode(C) = unicode(B)+unicode(D) 带来的问题。
双数组字典树(Double Array Trie)的更多相关文章
- sphinx索引分析——文件格式和字典是double array trie 检索树,索引存储 – 多路归并排序,文档id压缩 – Variable Byte Coding
1 概述 这是基于开源的sphinx全文检索引擎的架构代码分析,本篇主要描述index索引服务的分析.当前分析的版本 sphinx-2.0.4 2 index 功能 3 文件表 4 索引文件结构 4. ...
- 中文分词系列(二) 基于双数组Tire树的AC自动机
秉着能偷懒就偷懒的精神,关于AC自动机本来不想看的,但是HanLp的源码中用户自定义词典的识别是用的AC自动机实现的.唉-没办法,还是看看吧 AC自动机理论 Aho Corasick自动机,简称AC自 ...
- 【转】B树、B-树、B+树、B*树、红黑树、 二叉排序树、trie树Double Array 字典查找树简介
B 树 即二叉搜索树: 1.所有非叶子结点至多拥有两个儿子(Left和Right): 2.所有结点存储一个关键字: 3.非叶子结点的左指针指向小于其关键字的子树,右指针指向大于其关键字的子树: 如: ...
- 中文分词系列(一) 双数组Tire树(DART)详解
1 双数组Tire树简介 双数组Tire树是Tire树的升级版,Tire取自英文Retrieval中的一部分,即检索树,又称作字典树或者键树.下面简单介绍一下Tire树. 1.1 Tire树 Trie ...
- double array trie 插入结点总结
双数组Trie树索引的可操作性研究.pdf 提示:任一状态点的移动,会影响其Trie树中父节点的base值的选择以及兄弟结点位置的变动,而兄弟结点的移动又须变更相应的子节点的check值. 设待插入的 ...
- 双01字典树最小XOR(three arrays)--2019 Multi-University Training Contest 5(hdu杭电多校第5场)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6625 题意: 给你两串数 a串,b串,让你一一配对XOR使得新的 C 串字典序最小. 思路: 首先这边 ...
- [一本通学习笔记] 字典树与 0-1 Trie
字典树中根到每个结点对应原串集合的一个前缀,这个前缀由路径上所有转移边对应的字母构成.我们可以对每个结点维护一些需要的信息,这样即可以去做很多事情. #10049. 「一本通 2.3 例 1」Phon ...
- Double Array Trie 的Python实现
不多介绍,可自行Google,或者其它关键词: "datrie" 放代码链接: double_array_trie.py 因为也是一段学习代码,参考的文章都记在里面了,主要参考gi ...
- 双数组Trie树(DoubleArrayTrie)Java实现
http://www.hankcs.com/program/java/%E5%8F%8C%E6%95%B0%E7%BB%84trie%E6%A0%91doublearraytriejava%E5%AE ...
随机推荐
- 单调队列+线性dp题Watching Fireworks is Fun (CF372C)
一.Watching Fireworks is Fun(紫题) 题目:一个城镇有n个区域,从左到右1编号为n,每个区域之间距离1个单位距离节日中有m个烟火要放,给定放的地点ai,时间ti当时你在x,那 ...
- xuexi
1.内存的编址方法就是内存地址与内存单元格一一对应且永久绑定.计算机的cpu只认识内存地址,不关心内存单元格的位置和内容.通过硬件的设计来达到通过内存地址找到内存单元格. 2.内存的编址是以字节为单位 ...
- Kibana详细入门教程
Kibana详细入门教程 目录 一.Kibana是什么 二.如何安装 三.如何加载自定义索引 四.如何搜索数据 五.如何切换中文 六.如何使用控制台 七.如何使用可视化 八.如何使用仪表盘 一.K ...
- Python操作CSV和Excel
概述 csv是最通用的文件格式,本质是文本文件,用记事本即可打开.同一行中每个字段间用逗号分隔,在csv中显示的是在不同单元格中,在记事本中显示的是一行中用逗号分隔. xls是excel专用格式,是二 ...
- Linux运维学习第一周记
1 当年白岳伴清游, 2 江石台空一苇浮. 3 缥渺临风闻郢曲, 4 殷勤歧路看吴钩. 老气横秋方知世间沧桑! 以前一直忙,没有时间沉浸下来学习,一直都是浮着. 至此大疫,给生命按下了暂停键. 踏踏实 ...
- windows注册redis为服务,zookeeper为服务
windows注册redis为服务,zkserver为服务 1.redis部分 通过redis内置工具安装 进入redis安装目录 1.shift+鼠标右键打开菜单,点击"在此处打开命令窗口 ...
- C#Socket通讯(1)
前言 因为自己需要开发一款自己的游戏,因为设计网络方面,所以我找了很多的资料,再加上考虑到Unity游戏客户端直接连接数据库很容易导致数据库泄露,再加上网上很多的服务端都是用控制台做的,所以我就想做个 ...
- linux设置systemctl 启动脚本
centos 7 服务的systemctl 脚本一般存在:/usr/lib/systemd目录.目录下又分为system,和user之分, /usr/lib/systemd/system #系统服务, ...
- airtest本地连接和远程连接
一.本地连接 # -*- coding: utf-8 -*-# from poco.drivers.android.uiautomation import AndroidUiautomationPoc ...
- Linux系列:快捷键、目录结构、用户目录
一.快捷键 1.历史命令 查看历史命令:history [root@centos-master ~]# history 1 2020-10-25 21:03:39 2 2020-09-17 20:43 ...