当前位置:   article > 正文

字典树实现_手把手教学 | 双数组字典树

字典树的应用 如何用双数组实现

75b563f35eda0ca65240f979d09a419b.png

Natural language processing

an important direction in the field of 

computer science and artificial intelligence.

本篇内容结合HanLP中的实现方法,通过python代码实现,对字典树数据结构进行深入学习(对字典树和AC自动机不了解的读者可以看看另一篇推文十分钟学会AC自动机)

dacef41c09059061620b7c5b4370b836.png

字典树

1.HanLP字典树结构

结构示意图:

585ad593888b61005e04be49177820be.png

python代码构建结构:

节点包含值和子节点两个属性,存在值的节点表示某个词语尾节点,子节点为字典结构

15b596450f5aa8053b59c4ac75099827.png

2.HanLP字典树python实现

节点:

61678c2cdae2664d92c3038640aa28bb.png

字典树:

575c2abf83a7089697b5ae257295d262.png

判断是否包含:

294daf5940ab48408cefac5ab594a144.png

获取指定子节点:

bb370d551a3f4e3fe42d42be55a3610e.png

设置节点:

699aa9fd537f215506351147e77743d2.png

3.首字散列其余二分的字典树

结构示意图:

662f613ed7c69846723518d85cdfd959.png

python代码构建结构:

0dcaa9a86528d209889a8b274c06849b.png

4.python实现首字散列其余二分的字典树

由于python中不存在字符类型,字符串的hash算法在64位系统上,位数为64位整数,但Unicode字符总共才136690个,参考HanLP中java实现,用java中字符的散列函数,输出的区间为[0,65535]内的整数,因此将hash后的值截取到5位长度:

977ce101ca0f2f873aaf64304e522d3f.png

定义节点:

5c477b08471aa26e6ef9a076abfbeb8a.png

子节点的添加方法:

78ed5b0a19524a6bacf9c60ba588225d.png

子节点的查询方法:

43463f774e24040ed8df4ef0d6aeff15.png

实现二分查询:

d292c1ec78d19c7035c98ba2e543e4e8.png

定义字典树BinTrie:

63b8630c7e187fafd5a9704c9d12d721.png

字典树的增加方法:

af5405907a69a7fea310e9cb97dbff62.png

字典树的查询方法:

519bb5e54a1713766fcfbfccd547a9fa.png

557eaa18a848609846ee8a74daeb8e11.png

简单运行:

3c7275b4ecfdc2f886807b441239770a.png

结果:

1890470c01da2ae03f284bee684c8542.png

并且HanLP作者何晗已经验证了该数据结构进行最长匹配方法的中文分词,万字/秒比普通trie树有所提升(如逆向匹配算法,从原135万字/秒提升到了210万字/秒)。

5.首字散列其余二分的字典树的优化使用

其实,对于扫描"自然语言处理"该句分词时,原方法用_contains_方法是依次查找"自"、"自然"、"自然语"、"自然语言"等词语是否在词典中。然而,若"自然"不存在,则以"自然"开头的词语也都不存在了。因此考虑用该方式优化分词:当状态转移失败时,则提前终止扫描,进而节省时间。

HanLP作者何晗验证了该方式进行最长匹配方法的中文分词,万字/秒比普通方法有大幅提升(如正向匹配算法,从原213万字/秒提升到了1176万字/秒,比最初的64万字/秒提高了2个数量级)。

6.双数组字典树

核心思想:

由base和check两个数组构成(base和check的索引表示一个状态),缩短状态转移过程的时间。

具体的,当状态b接受字符c转移到状态p时,满足条件(状态由整数下标表示):

state[p] = base[state[b]] + index[c]

check[state[p]] == state[b]

若条件不满足则转换失败。

如:当前状态自然(例如state[自然]=1),若想判断是否可以转移到状态自然人,先执行state[自然人] = base[state[自然]] + index[人] = base[1] + index[人],然后判断check[state[自然人]] == state[自然]是否成立即可,仅需一次加法和整数比较就能进行状态转移,转移过程为常数事件。

base和check构建过程*

假设有一个字符集仅有 {a, b, c} 有单词 a, ab, bbc, bc 构建一个 DAT,首先给字符集编码 index[a] = 1; index[b] = 2; index[c] = 3;

1.构建a:

e5082dd75fe8c7ec6a6ef92a6fad579c.png

2.构建ab:

f6fb07d49e2601b93e56e8720a58bb2c.png

3.构建bbc:

构建b时,发现冲突:

471069a2017796ea4a3316465315c353.png

构建bb:

03da418131a54792ed0d8418cfd6842c.png

构建bbc:

5de7c095cdab02c140b64d00efb9f2f0.png

4.构建bc:

c59c25eed964022767cafebf6c341ada.png

5.搜索bbc是否存在:

1.state[b] = base[state[null]] + index[b] = 2

检查check[state[b]] == state[null] 即 check[2] == 0为true

2.state[bb] = base[state[b]] + index[b] = 1 + 2 = 3

检查check[state[bb]] == state[b] 即 check[3] == 2为true

3.state[bbc] = base[state[bb]] + index[c] = 2 + 3 = 5

检查check[state[bbc]] == state[bb] 即 check[5] == 3为true

则字典中存在bbc

7.python实现双数组字典树

定义Node:

c49523c412edaa2934a94dea13f4dd8f.png

定义HashMap:

fce9ce99f60ca5ea9a3d45bf1bc4e59e.png

HashMap新增:

4c265b30adad0d61093c5e5f1930060d.png

HashMap判断是否存在:

ed9cc1c372de5d2c86651601d2a610b3.png

HashMap获取:

bd3cd964a65c111a658cbd3afd3abbc3.png

增强版HashMap:

4fbd25ecc2342fee91a8faabb2b11ea1.png

定义双数组字典树:

cfbf5f25a87b2957717d5c6b88902cd1.png

双数组字典树构建过程:

0a1da8e04e48921153c9224e436a2fdc.png

85e78bf05e70617c043f1c6b49d8b90c.png

6d2666b29e3150b100843f296273ee67.png

79819c92170e59b18d77dcb70038af0c.png

a5af676ab905b81dcac0d201f2cf92e5.png

双数组字典树判断是否存在:

1427373b19070ec627c932b86b338cfc.png

HanLP作者何晗验证了双数组字典树进行最长匹配方法的中文分词,万字/秒比普通方法有大幅提升(完全切分从BinTrie的741万字/秒提升到了1606万字/秒,正向最长匹配从1176万字/秒提升到了3584万字/秒)。

PROMPT

温馨提示

作者原创 | 未经授权请勿转载

b03ea1e8e588f3d1962eb1d6212433ec.png

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/443372
推荐阅读
相关标签
  

闽ICP备14008679号