我是最差的NLPer之Trie树

2020 年 5 月 16 日 AINLP


一 :今日吐槽


作为一个没有系统学过CS四大课程的NLPer,凭借一手百度+CTRL-C+CTRL-V的技能,混迹于技术中心,实属不易。


不要问我线程安全,问就是不会。

不要问我TCP三次握手,问就是不会。

不要问我二叉树的后序遍历,问就是不会。


知乎上有个问题:你见过最差的算法工程师能差到什么程度?


有个回答:


"我听过一个江湖潜规则,每一个大公司团队都会招一个特别不着调,水平也不行,智商也比较低的算法工程师,用来背低绩效。


当组里成员感到职业发展太难的时候,就会下意识看看那个人,这样,心里就会有莫大的安慰。


我当时下意识地看看周围的同事,发现他们都不符合这些描述。"


我也回头看了看钰轩君、费马君和其他同事,发现他们都不符合这些描述。



二:内容预告


自然语言处理还不是深度学习的天下。


传统机器学习算法如HMM、CRF、Kmeans,传统数据结构和算法如Trie树、AC自动机、动态规划,都还大有用武之地。


最近就遇到一个问题:如何做敏感词过滤。


网上查了一下资料,方案有两个:确定有限状态自动机(DFA)和AC自动机。


AC自动机又涉及Trie树和KMP算法。


这让我进一步想到NLP中常用的其他数据结构和算法:


  • query纠错中的编辑距离

  • ROUGE-L中的最长公共子序列

  • 序列标注中的维特比算法

  • word2vec中的哈弗曼树

  • 海量文本求topk相似的局部敏感哈希


看来,为了摆脱一年后失业的悲惨命运,传统的数据结构和算法是不得不学了。


本文整理Trie树的python代码实现。


本来还想基于Trie树,实现搜索引擎的自动联想功能,可是想想520还是一个人过,我还是放过自己吧。


本文关注以下三方面的内容:


  • 什么是Trie树

  • Trie树的时间复杂度和空间复杂度

  • Trie树的python实现



三:什么是Trie树


01

Trie树的自我介绍


大家好,我是Trie树,你们也可以叫我字典树或前缀树。


我擅长处理字符串匹配问题,即给定N条字符串的集合,我可以快速判断某条字符串是否在这个集合中。


比如字符串集合中有8个词语:


["白天","白日梦","白发","白发红颜","黑夜","黑白", "黑白颠倒","黑白分明"]


我们需要判断 "黑白颠倒" 这个词语是否在这个集合中。


那么我是怎么处理这个问题的呢?


首先我会以树的格式存储这8个词语,如下图。


红色节点的意思是,从上往下到这个节点,是一个词语。



以字典的格式存储,如下:


{'白': {'发': {'Exist'True'红': {'颜': {'Exist'True}}},
       '天': {'Exist'True},
       '日': {'梦': {'Exist'True}}},
 '黑': {'夜': {'Exist'True},
       '白': {'Exist'True,
             '分': {'明': {'Exist'True}},
             '颠': {'倒': {'Exist'True}}}}}


查找时,我首先把 “黑白颠倒” 拆成【黑,白,颠,倒】三个字。


然后去字典中进行匹配,首先匹配中【黑】字,那么继续往下匹配,发现第一条路径的子节点为【白】,匹配中。


然后继续往下匹配,依次匹配中【颠】、【倒】,于是判断这个词语在字符串集合中。


可以看到,由于很多词有共同的前缀 【黑】,所以第一个字符匹配中【黑】之后,后面字符的匹配,我只在以【黑】开头的这棵右子树上去匹配就行,匹配的速度更快。


如果用纯粹遍历的方法,怎么查找呢?


那我还得拿 “黑白颠倒” 这个词,和以【白】字开头的四个词去比较,速度就慢了。


02

Trie树的特点、复杂度和适用场景


通过以上简单的例子,你大概也可以总结出我作为巨蟹座的三个特点:


  • 根节点(ROOT)不包含字符,除根节点以外的每个节点只包含一个字符。

  • 从根节点到特殊标记节点(红色节点),路径上经过的字符连接起来,为该特殊节点对应的完整字符串。 

  • 每个节点的所有子节点(字符)不相同。


我坚决贯彻以空间换时间的方针政策,利用字符串的公共前缀,减少无谓的字符串比较,从而降低查询的时间复杂度。


假设字符集的总数为m(比如英文中只有26个字母),如果需要存储的字符串的最大长度为n,那么树的每个节点出度为m(即每个节点可能的子节点数量为m),Trie树的高度为n。


我的空间复杂度为O(m^n),是不是很恐怖?


但是正由于每个节点的出度为m,所以我能够每次顺着分支(最多m个)往下查找,而不需要遍历所有的字符串。


我的时间复杂度为O(n)。


也就是说,如果要查询的字符串长度为n,那么查n次,就可以查到。


每个班都有一个灵活的胖子。


我通常出现在搜索引擎系统中,用于搜索提示和词频统计,也作为AC自动机的基本数据结构,用于敏感词过滤。




四:Trie树的实现


下面就用Python来写一棵Trie树,实现插入和查找字符串的功能。


#coding:utf-8
class Trie:

    def __init__(self):
        """
        Trie树。
        如果到该节点为完整字符串,则标记为 Exist
        """

        self.root = {}
        self.word_end = "Exist"

    def insert(self, word):
        """
        插入单词。
        如果某字符不是子节点,则作为子节点插入;
        如果某字符已经是子节点,则共享该节点。
        """

        curNode = self.root
        for c in word:
            if not c in curNode:
                curNode[c] = {}
            curNode = curNode[c]

        curNode[self.word_end] = True

    def search(self, word):
        """
        查找存在字典树中的单词
        如果查找到最后一个字符,没有标记为 Exist,则不存在
        如果最后一个字符,被标记为 Exist,则查询成功。
        """

        curNode = self.root
        for c in word:
            if not c in curNode:
                return False
            curNode = curNode[c]

        if self.word_end not in curNode:
            return False

        return True

    def startsWith(self, prefix):
        """
        判断输入的词是否为字典树中的前缀
        如果输入词的每个字符都是子节点,则为前缀
        """

        curNode = self.root
        for c in prefix:
            if not c in curNode:
                return False
            curNode = curNode[c]
        return True


然后来测试一下:


if __name__ == "__main__":

    trie = Trie()
    words = ["白天","白日梦","白发","白发红颜","黑夜","黑白","黑白颠倒","黑白分明"]
    for word in words:
        trie.insert(word)
    print(trie.search("白天"))


字典树为:


{'白': {'发': {'Exist'True'红': {'颜': {'Exist'True}}},
       '天': {'Exist'True},
       '日': {'梦': {'Exist'True}}},
 '黑': {'夜': {'Exist'True},
       '白': {'分': {'明': {'Exist'True}}, '颠': {'倒': {'Exist'True}}}}}


返回:True。



五:Trie树的白板编程


拿出一张A4纸,来一个白板编程。



最近不知道自己在干什么,这篇写得很水,各位看官就当是在看笑话吧。

END

推荐阅读

AINLP年度阅读收藏清单

中文命名实体识别工具(NER)哪家强?

学自然语言处理,其实更应该学好英语

斯坦福大学NLP组Python深度学习自然语言处理工具Stanza试用

太赞了!Springer面向公众开放电子书籍,附65本数学、编程、机器学习、深度学习、数据挖掘、数据科学等书籍链接及打包下载

数学之美中盛赞的 Michael Collins 教授,他的NLP课程要不要收藏?

自动作诗机&藏头诗生成器:五言、七言、绝句、律诗全了

模型压缩实践系列之——bert-of-theseus,一个非常亲民的bert压缩方法

这门斯坦福大学自然语言处理经典入门课,我放到B站了

征稿启示 | 稿费+GPU算力+星球嘉宾一个都不少

关于AINLP

AINLP 是一个有趣有AI的自然语言处理社区,专注于 AI、NLP、机器学习、深度学习、推荐算法等相关技术的分享,主题包括文本摘要、智能问答、聊天机器人、机器翻译、自动生成、知识图谱、预训练模型、推荐系统、计算广告、招聘信息、求职经验分享等,欢迎关注!加技术交流群请添加AINLPer(id:ainlper),备注工作/研究方向+加群目的。


登录查看更多
1

相关内容

在数学和计算机科学之中,算法(Algorithm)为一个计算的具体步骤,常用于计算、数据处理和自动推理。精确而言,算法是一个表示为有限长列表的有效方法。算法应包含清晰定义的指令用于计算函数。 来自维基百科: 算法
耶鲁大学《分布式系统理论》笔记,491页pdf
专知会员服务
44+阅读 · 2020年7月29日
深度学习自然语言处理概述,216页ppt,Jindřich Helcl
专知会员服务
209+阅读 · 2020年4月26日
【推荐系统/计算广告/机器学习/CTR预估资料汇总】
专知会员服务
86+阅读 · 2019年10月21日
NLP基础任务:文本分类近年发展汇总,68页超详细解析
专知会员服务
73+阅读 · 2019年10月19日
【面经】字节AI Lab-NLP算法热乎面经
深度学习自然语言处理
14+阅读 · 2020年3月29日
将Python用于NLP:Pattern 库简介
Python程序员
15+阅读 · 2019年6月7日
神经网络训练tricks
极市平台
6+阅读 · 2019年4月15日
自然语言处理(NLP)知识结构总结
AI100
51+阅读 · 2018年8月17日
Python NLP 入门教程
大数据技术
19+阅读 · 2017年10月24日
Python 自然语言处理(NLP)工具库汇总
数据挖掘入门与实战
7+阅读 · 2017年9月25日
NLP自然语言处理(二)——基础文本分析
乐享数据DataScientists
12+阅读 · 2017年2月7日
NLP自然语言处理(一)——jieba分词(R vs. python)
乐享数据DataScientists
6+阅读 · 2017年1月15日
Visualizing and Measuring the Geometry of BERT
Arxiv
7+阅读 · 2019年10月28日
Universal Transformers
Arxiv
5+阅读 · 2019年3月5日
Arxiv
3+阅读 · 2018年12月18日
Arxiv
3+阅读 · 2018年10月18日
VIP会员
相关资讯
【面经】字节AI Lab-NLP算法热乎面经
深度学习自然语言处理
14+阅读 · 2020年3月29日
将Python用于NLP:Pattern 库简介
Python程序员
15+阅读 · 2019年6月7日
神经网络训练tricks
极市平台
6+阅读 · 2019年4月15日
自然语言处理(NLP)知识结构总结
AI100
51+阅读 · 2018年8月17日
Python NLP 入门教程
大数据技术
19+阅读 · 2017年10月24日
Python 自然语言处理(NLP)工具库汇总
数据挖掘入门与实战
7+阅读 · 2017年9月25日
NLP自然语言处理(二)——基础文本分析
乐享数据DataScientists
12+阅读 · 2017年2月7日
NLP自然语言处理(一)——jieba分词(R vs. python)
乐享数据DataScientists
6+阅读 · 2017年1月15日
相关论文
Visualizing and Measuring the Geometry of BERT
Arxiv
7+阅读 · 2019年10月28日
Universal Transformers
Arxiv
5+阅读 · 2019年3月5日
Arxiv
3+阅读 · 2018年12月18日
Arxiv
3+阅读 · 2018年10月18日
Top
微信扫码咨询专知VIP会员