自然语言处理学习笔记(六)————字典树

目录

1.字典树

(1)为什么引入字典树

(2)字典树定义

(3)字典树的节点实现

(4)字典树的增删改查

DFA(确定有穷自动机)

(5)优化


1.字典树

(1)为什么引入字典树

        匹配算法的瓶颈之一在于如何判断集合(词典)中是否含有字符串。如果用有序集合TreeMap)的话,复杂度是o(logn) ( n是词典大小);如果用散列表( Java的HashMap. Python的dict )的话,账面上的时间复杂度虽然下降了,但内存复杂度却上去了。有没有速度又快、内存又省的数据结构呢?这就是字典树。

(2)字典树定义

        字符串集合常用字典树存储,这是一种字符串上的树形数据结构。字典树中每条边都对应一个字,从根节点往下的路径构成一个个字符串。字典树并不直接在节点上存储字符串,而是将词语视作根节点到某节点之间的一条路径,并在终点节点上做个标记"该节点对应词语的结尾".字符串就是一条路径,要查询一个单词,只需顺着这条路径从根节点往下走。如果能走到特殊标记的节点,则说明该字符串在集合中,否则说明不存在。一个典型的字典树如下图所示所示。

         其中,蓝色标记着该节点是一个词的结尾,数字是人为的编号。按照路径我们可以得到如下表所示:

词语路径
入门0-1-2
自然0-3-4
自然人0-3-4-5
自然语言0-3-4-6-7
自语0-3-8

        当词典大小为 n 时,虽然最坏情况下字典树的复杂度依然是O(logn) (假设子节点用对数复杂度的数据结构存储,所有词语都是单字),但它的实际速度比二分查找快。这是因为随着路径的深入,前缀匹配是递进的过程,算法不必比较字符串的前缀。 

(3)字典树的节点实现

        我们要用python类来实现字典树,首先要想明白字典树的基本性质,对于每个节点来说,我们需要知道它对应的子节点和对应的边。如果要实现映射的话,还需要知道自己对应的值。·约定用值为None表示节点不对应词语,虽然这样就不能插入值为None的键了,但实现起来更简单。在_add_child方法中,先检查是否已经存在字符char对应的child,然后根据overwrite来决定是否覆盖child的值。通过这样,就可以把子节点连接到父节点上去。

class Node(object):
    def __init__(self, value):
        self._children = {} # 表示该节点下的分支(孩子,子节点)有哪些,用字典存储:char为键,表示子节点的字。字典的值为分支位置
        self._value = value # 理解为节点对应的值,value相当于表示从根节点到这里这是个词,不是词的话就是none,没有含义。
    
    def _add_child(self, char, value, overwrite=False):  # overwrite为true就是重写,false就是不重写。
        child = self._children.get(char)  # 得到该节点在char这条边的子节点
        if child = None:                  # 如果该节点在这个char这没有分支
            child = Node(value)           # 则新建一个char的分支
            self._children[char] = child  # 把父节点的char分支位置对应到新建的节点位置,这样就连接起来了。
        elif overwrite:
            child._value = value # 重写overwrite覆盖掉原来的值
        return child  # 返回的是child node的位置,即子节点位置

视频:  0203字典树Node_哔哩哔哩_bilibili 0203字典树Node_哔哩哔哩_bilibili 

比如在字典树中插入“入门”词语 

插入“自然人”词语 

插入“自然”词语

(4)字典树的增删改查

        "删改查"其实是一回事,都是查询。删除操作就是将终点的值设为None而已,修改操作无非是将它的值设为另一个值而已。从确定有限状态自动机的角度来讲,每个节点都是一个状态,状态表示当前已查询到的前缀。,从父节点到子节点的转移可以看作一个事件(状态转移)。我们向父节点查询是否有满足状态的边,如果有,则转移状态,当全部转移后,我们会询问该节点(状态)是否为蓝色节点,若是,则查询成功。

DFA(确定有穷自动机)

概念:从一个状态通过一系列事件转换到另一个状态

 【过程】:

  • 初始状态为空,当触发事件“匹”时转换到状态“匹”;
  • 触发事件“配”,转换到状态“匹配”;
  • 依次类推,直到转换为最后一个状态“匹配关键词”。

        ”增加键值对“其实还是查询,只不过在状态转移失败的时候,则创建相应的子节点,保证转移成功。

字典树的完整实现如下:

# 继承于上面的node类
class Trie(Node):
    # _init_可理解为“构造函数”,在对象初始化的时候调用,使用传入的参数初始化该实例。
    def __init__(self) -> None:
        super().__init__(None)

    # _contains_用于自定义容器类型,定义调用in和 not in来测试成员是否存在的时候所产生的行为。
    def __contains__(self, key):
        return self[key] is not None # is not None语法可以认为判断一个变量是否为None

    # __getitem_用于自定义容器类型,定义当某一项被访问时,使用 self[key]所产生的行为。
    def __getitem__(self, key):
        state = self
        for char in key:
            state = state._children.get(char)
            if state is None:
                return None
        return state._value

    # _setitem_用于自定义容器类型,定义执行 self[key]=value 时产生的行为。
    def __setitem__(self, key, value):
        state = self
        # enumerate() 函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在 for 循环当中。
        for i, char in enumerate(key):  
            if i < len(key) - 1:
                state = state._add_child(char, None, False)
            else:
                state = state._add_child(char, value, True)

测试:

if __name__ == '__main__':
    trie = Trie()
    # 增
    trie['自然'] = 'nature'
    trie['自然人'] = 'human'
    trie['自然语言'] = 'language'
    trie['自语'] = 'talk    to oneself'
    trie['入门'] = 'introduction'
    assert '自然' in trie   # assert是python断言语法,用于判断一个表达式,在表达式条件为 false 的时候触发异常。
    # 删
    trie['自然'] = None
    assert '自然' not in trie
    # 改
    trie['自然语言'] = 'human language'
    assert trie['自然语言'] == 'human language'
    # 查
    assert trie['入门'] == 'introduction'

(5)优化

        字典树的数据结构在以上的切分算法中已经很快了,但还有一些基于字典树的算法改进,把分词速度推向了千万字每秒的级别,主要按照以下递进关系优化:

  • 首字散列其余二分的字典树
  • 双数组字典树
  • AC自动机(多模式匹配)
  • 基于双数组字典树的AC自动机

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/66615.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

BigDecimal使用总结

BigDecimal Java在java.math包中提供的API类BigDecimal&#xff0c;用来对超过16位有效位的数进行精确的运算。双精度浮点型变量double可以处理16位有效数。 在实际应用中&#xff0c;需要对更大或者更小的数进行运算和处理。float和double只能用来做科学计算或者是工程计算&a…

Leetcode-每日一题【剑指 Offer 06. 从尾到头打印链表】

题目 输入一个链表的头节点&#xff0c;从尾到头反过来返回每个节点的值&#xff08;用数组返回&#xff09;。 示例 1&#xff1a; 输入&#xff1a;head [1,3,2]输出&#xff1a;[2,3,1] 限制&#xff1a; 0 < 链表长度 < 10000 解题思路 1.题目要求我们从尾到头反过…

Python爬虫在电商数据挖掘中的应用

作为一名长期扎根在爬虫行业的专业的技术员&#xff0c;我今天要和大家分享一些有关Python爬虫在电商数据挖掘中的应用与案例分析。在如今数字化的时代&#xff0c;电商数据蕴含着丰富的信息&#xff0c;通过使用爬虫技术&#xff0c;我们可以轻松获取电商网站上的产品信息、用…

401 · 排序矩阵中的从小到大第k个数

链接&#xff1a;LintCode 炼码 - ChatGPT&#xff01;更高效的学习体验&#xff01; 题解&#xff1a; 九章算法 - 帮助更多程序员找到好工作&#xff0c;硅谷顶尖IT企业工程师实时在线授课为你传授面试技巧 class Solution { public:/*** param matrix: a matrix of intege…

mysql再docker中运行,直接在实体机上运行mysql命令初始化数据库数据

背景 项目上我们使用docker安装mysql&#xff0c;项目启动的时候需要利用sql语句初始化数据。 直接在实体上是识别不到mysql命令的。 实现方式 实现方式1&#xff1a;在docker容器内部执行sql语句 1. 将sql文件上传到容器内 docker cp /root/1.sql d5:/home/ 说明&#…

【小梦C嘎嘎——启航篇】类和对象(中篇)

【小梦C嘎嘎——启航篇】类和对象&#xff08;中篇&#xff09;&#x1f60e; 前言&#x1f64c;类的6个默认成员函数构造函数析构函数拷贝构造函数拷贝构造函数的特性有哪些&#xff1f;既然编译器可以自动生成一个拷贝构造函数&#xff0c;为什么我们还要自己设计实现呢&…

外卖点餐小程序开源源码——支持扫码点餐

一套支持店内扫码点餐、外卖点餐配送于一体的餐饮系统&#xff0c;支持商家创建优惠券&#xff0c;支持商家自定义打印机功能&#xff0c;支持商家财务管理&#xff0c;支持商户菜品管理&#xff0c;支持菜品自定义分类&#xff0c;支持商家招募骑手入驻功能。系统基于thinkphp…

【Axure动态面板】利用动态面板实现树形菜单的制作

利用动态面板&#xff0c;简单制作高保真的树形菜单。 一、先看效果 https://1poppu.axshare.com 二、实现思路 1、菜单无非就是收缩和展开&#xff0c;动态面板有个非常好的属性&#xff1a;fit to content&#xff0c;这个属性的含义是&#xff1a;面板的大小可以根据内容多少…

HCIP的OSPF综合实验

一、实验要求 1、R4为ISP&#xff0c;其上只能配置IP地址: R4与其他所有直连设备间使用公有 2、R3—R5/6/7为MGRE环境&#xff0c;R3为中心站点 3、整个OSPF环境IP地址为172.16.0.0/16 4、所有设备均可访问R4的环回 5、减少LSA的更新量&#xff0c;加快收敛&#xff0c;保障更…

《HeadFirst设计模式(第二版)》第七章代码——外观模式

代码文件目录&#xff1a; Subsystem: Amplifier package Chapter7_AdapterAndFacadePattern.FacadePattern.Subsystem;/*** Author 竹心* Date 2023/8/8**///扬声器 public class Amplifier {int volume 0;//音量public void on(){System.out.println("The amplifier …

NodeJs执行Linux脚本

&#xff08;我们活着不能与草木同腐&#xff0c;不能醉生梦死&#xff0c;枉度人生&#xff0c;要有所作为。——方志敏&#xff09; 为什么需要使用NodeJs执行Linux脚本 linux的sh脚本命令编写复杂&#xff0c;在不熟悉linux交互式命令的情况下&#xff0c;使用高级编程语言…

【论文研读】MARLlib 的架构分析

【论文研读】MARLlib: A Scalable Multi-agent Reinforcement Learning Library 和尚念经 多智能体强化学习框架研究。 多智能体强化学习库。 多智能体强化学习算法实现。 多智能体强化学习环境的统一化&#xff0c;标准化。 多智能体强化学习算法解析。 多智能体强化学习 算法…

Android 面试重点之Framework (Handler篇)

近期在网上看到不少Android 开发分享的面试经验&#xff0c;我发现基本每个面经中多多少少都有Framework 底层原理的影子。它也是Android 开发中最重要的一个部分&#xff0c;面试官一般会通过 Framework底层中的一些逻辑原理由浅入深进行提问&#xff0c;来评估应聘者的真实水…

小型双轮差速底盘机器人实现红外跟随功能

1. 功能说明 本文示例将实现R023样机小型双轮差速底盘跟随人移动的功能。在小型双轮差速底盘前方按下图所示安装3个 近红外传感器&#xff0c;制作一个红外线发射源&#xff0c;实现当红外发射源在机器人的检测范围内任意放置或移动时&#xff0c;机器人能追踪该发射源。 2. 电…

Teams Room视频会议室方案

需求背景&#xff1a; 适合在40平米的会议室参加Teams视频会议&#xff0c;会议桌周围可以坐20人&#xff0c;要求&#xff1a; 1&#xff0c;操作简单&#xff0c;一键入会Teams Room&#xff1b; 2&#xff0c;任何人带上自己的笔记本电脑&#xff0c;可以分享电脑画面&#…

Linux CEF(Chromium Embedded Framework)源码下载编译详细记录

Linux CEF&#xff08;Chromium Embedded Framework&#xff09;源码下载编译 背景 由于CEF默认的二进制分发包不支持音视频播放&#xff0c;需要自行编译源码&#xff0c;将ffmpeg开关打开才能支持。这里介绍的是Linux平台下的CEF源码下载编译过程。 前置条件 下载的过程非…

搭建Repo服务器

1 安装repo 参考&#xff1a;清华大学开源软件镜像站:Git Repo 镜像使用帮助 2 创建manifest仓库 2.1 创建仓库 git init --bare manifest.git2.2 创建default.xml文件 default.xml文件内容&#xff1a; <?xml version"1.0" encoding"UTF-8" ?…

MySQL语法2

DQL语句介绍 DQL是数据查询语言&#xff0c;用来查询数据库中表的记录 DQL-基本查询语句 SELECT 字段列表 FROM 表名列表 WHERE 条件列表 GROUP BY 分组字段列表 HAVIMG 分组后条件列表 ORDER BY 排列字段列表 LIMIT 分页参数 讲解过程&#xff1a;基本查询、条件查询…

博客网站添加复制转载提醒弹窗Html代码

网站如果是完全禁止右键&#xff08;复制、另存为等&#xff09;操作&#xff0c;对用户来说体验感会降低&#xff0c;但是又不希望自己的原创内容直接被copy&#xff0c;今天飞飞和你们分享几行复制转载提醒弹窗Html代码。 效果展示&#xff1a; 复制以下代码&#xff0c;将其…