【Algorithms 4】算法(第4版)学习笔记 23 - 5.4 正则表达式

文章目录

    • 前言
    • 参考目录
    • 学习笔记
      • 1:正则表达式
      • 1.1:表示
      • 1.2:快捷表示
      • 2:正则表达式与非确定有限状态自动机 REs and NFAs
      • 2.1:二元性
      • 2.2:模式匹配实现
      • 2.3:非确定有限状态自动机 Nondeterministic finite-state automata
      • 2.4:非确定性
      • 3:NFA 模拟
      • 3.1:demo 演示
      • 3.2:Java 实现
      • 3.3:分析
      • 4:NFA 构造
      • 4.1:构造与正则表达式对应的 NFA
      • 4.2:实现
      • 4.3:demo 演示
      • 4.4:Java 实现
      • 4.5:分析
      • 5:非正则表达式
      • 6:背景
      • 7:小结

前言

本篇主要内容包括:正则表达式非确定有限状态自动机 NFA

建议在学习本篇之前先行学习或回顾上一篇子字符串查找的内容。

参考目录

  • B站 普林斯顿大学《Algorithms》视频课
    (请自行搜索。主要以该视频课顺序来进行笔记整理,课程讲述的教授本人是该书原版作者之一 Robert Sedgewick。)
  • 微信读书《算法(第4版)》
    (本文主要内容来自《5.4 正则表达式》)
  • 官方网站
    (有书本配套的内容以及代码)

学习笔记

注1:下面引用内容如无注明出处,均是书中摘录。
注2:所有 demo 演示均为视频 PPT demo 截图。
注3:如果 PPT 截图中没有翻译,会在下面进行汉化翻译,因为内容比较多,本文不再一一说明。

1:正则表达式

1.1:表示

![L20-54RegularExpressions_06]

对应书本章节:《5.4.1 使用正则表达式描述模式》

  • 5.4.1.1 连接操作
  • 5.4.1.2 或操作
  • 5.4.1.3 闭包操作
  • 5.4.1.4 括号

1.2:快捷表示

![L20-54RegularExpressions_07]

对应书本章节:《5.4.2 缩略写法》

  • 5.4.2.1 字符集描述符
  • 5.4.2.2 闭包的简写
  • 5.4.2.3 转义序列

2:正则表达式与非确定有限状态自动机 REs and NFAs

2.1:二元性

![L20-54RegularExpressions_16]

RE(正则表达式): 简洁描述一组字符串的方法。
DFA(确定有限状态自动机): 一种机器,用于判断给定的字符串是否属于预定义的字符串集合。

克林宁定理(Kleene’s theorem):

  • 对于任何确定有限状态自动机(DFA),都存在一个能够描述相同字符串集合的正则表达式(RE)。
  • 对于任何正则表达式(RE),都存在一个能够识别相同字符串集合的确定有限状态自动机(DFA)。

2.2:模式匹配实现

![L20-54RegularExpressions_18]

类似于 KMP 算法:

  • 不需要文本输入流回溯。
  • 确保二次时间复杂度(通常为线性时间)。

基础抽象概念: 非确定有限状态自动机(NFA)。

基本策略:[应用克林宁定理]

  • 从正则表达式构建 NFA。
  • 使用文本作为输入模拟 NFA。

2.3:非确定有限状态自动机 Nondeterministic finite-state automata

![image-20240402093803403]

对应书本章节:《5.4.4 非确定有限状态自动机》。

![image-20240402094701193]

也有可能进入错误状态并停滞:

![image-20240402095141143]

![image-20240402095201507]

2.4:非确定性

![L20-54RegularExpressions_23]

Q. 如何确定一个字符串是否被自动机所匹配?
DFA(确定有限状态自动机): 判定较为简单,因为对于每个状态和输入字符,恰好有一个适用的转换。
NFA(非确定有限状态自动机): 可能存在多个适用的转换;需要正确选择其中一个!

Q. 如何模拟 NFA?
A. 系统地考虑所有可能的转换序列来进行模拟。

3:NFA 模拟

3.1:demo 演示

![image-20240402163328370]

![image-20240402163446270]

该 demo 建议多观看几遍视频理解操作步骤。

3.2:Java 实现

edu.princeton.cs.algs4.NFA

![image-20240402164427969]
edu.princeton.cs.algs4.NFA#NFA

/**
     * Initializes the NFA from the specified regular expression.
     *
     * @param  regexp the regular expression
     */
    public NFA(String regexp) {
        this.regexp = regexp;
        m = regexp.length();
        Stack<Integer> ops = new Stack<Integer>();
        graph = new Digraph(m+1);
        for (int i = 0; i < m; i++) {
            int lp = i;
            if (regexp.charAt(i) == '(' || regexp.charAt(i) == '|')
                ops.push(i);
            else if (regexp.charAt(i) == ')') {
                int or = ops.pop();

                // 2-way or operator
                if (regexp.charAt(or) == '|') {
                    lp = ops.pop();
                    graph.addEdge(lp, or+1);
                    graph.addEdge(or, i);
                }
                else if (regexp.charAt(or) == '(')
                    lp = or;
                else assert false;
            }

            // closure operator (uses 1-character lookahead)
            if (i < m-1 && regexp.charAt(i+1) == '*') {
                graph.addEdge(lp, i+1);
                graph.addEdge(i+1, lp);
            }
            if (regexp.charAt(i) == '(' || regexp.charAt(i) == '*' || regexp.charAt(i) == ')')
                graph.addEdge(i, i+1);
        }
        if (ops.size() != 0)
            throw new IllegalArgumentException("Invalid regular expression");
    }

edu.princeton.cs.algs4.NFA#recognizes

/**
     * Returns true if the text is matched by the regular expression.
     *
     * @param  txt the text
     * @return {@code true} if the text is matched by the regular expression,
     *         {@code false} otherwise
     */
    public boolean recognizes(String txt) {
        DirectedDFS dfs = new DirectedDFS(graph, 0);
        Bag<Integer> pc = new Bag<Integer>();
        for (int v = 0; v < graph.V(); v++)
            if (dfs.marked(v)) pc.add(v);

        // Compute possible NFA states for txt[i+1]
        for (int i = 0; i < txt.length(); i++) {
            if (txt.charAt(i) == '*' || txt.charAt(i) == '|' || txt.charAt(i) == '(' || txt.charAt(i) == ')')
                throw new IllegalArgumentException("text contains the metacharacter '" + txt.charAt(i) + "'");

            Bag<Integer> match = new Bag<Integer>();
            for (int v : pc) {
                if (v == m) continue;
                if ((regexp.charAt(v) == txt.charAt(i)) || regexp.charAt(v) == '.')
                    match.add(v+1);
            }
            if (match.isEmpty()) continue;

            dfs = new DirectedDFS(graph, match);
            pc = new Bag<Integer>();
            for (int v = 0; v < graph.V(); v++)
                if (dfs.marked(v)) pc.add(v);

            // optimization if no states reachable
            if (pc.size() == 0) return false;
        }

        // check for accept state
        for (int v : pc)
            if (v == m) return true;
        return false;
    }

3.3:分析

![L20-54RegularExpressions_32]

对应书本命题 Q:

![image-20240402164943402]

4:NFA 构造

4.1:构造与正则表达式对应的 NFA

![L20-54RegularExpressions_34]

状态: 为正规表达式(RE)中的每个符号创建一个状态,同时添加一个接受状态。

![L20-54RegularExpressions_35]

连接操作: 从字母表中字符对应的当前状态添加匹配转换边至下一个状态。

![L20-54RegularExpressions_36]

括号: 从括号所在的状态添加一条 ε - 转换边至下一个状态。

![L20-54RegularExpressions_37]

闭包操作: 对于每一个运算符,添加三条 ε - 转换边。

![L20-54RegularExpressions_38]

或表达式: 对于每一个 |(逻辑或)操作符,添加两条 ε - 转换边。

4.2:实现

![L20-54RegularExpressions_39]

目标: 编写一个程序来构建 ε - 转换有向图。

挑战: 记忆左括号以实现闭包和逻辑或;记忆逻辑或符号 | 以实现逻辑或操作。

解决方案: 维护一个栈结构。

  • 遇到 ( 符号时:将 ( 入栈。
  • 遇到 | 符号时:将 | 入栈。
  • 遇到 ) 符号时:弹出与之配对的 ( 及其间的所有 | 符号;然后根据闭包和逻辑或的规则,添加相应的 ε - 转换边。

4.3:demo 演示

![image-20240402173630494]

4.4:Java 实现

![L20-54RegularExpressions_42]

4.5:分析

![L20-54RegularExpressions_43]

对应书本命题 R:

![image-20240402174323446]

5:非正则表达式

![L20-54RegularExpressions_53]

反向引用:

  • \1 表示法用于匹配先前已匹配到的子表达式。
  • 这一特性在典型的正则表达式实现中得到支持。

某些非正则表达式的例子:

  • 形如 ww 的字符串,其中 w 是任意字符串,例如 beriberi
  • 包含复合数量 1 的单字符字符串,例如 111111
  • 含有相同数量 0 和 1 的二进制字符串,例如 01110100
  • Watson-Crick 互补的回文串,例如 atttcggaaat

注解: 使用反向引用进行模式匹配的问题属于难解问题(不可行或计算复杂度较高)。

6:背景

![L20-54RegularExpressions_54]

抽象机、语言及非确定性概念:

  • 是计算理论的基础。
  • 自20世纪30年代以来就被深入研究。
  • 是现代编程语言的基础。

编译器:

  • 编译器是一种程序,负责将源程序翻译成机器码。
  • KMP 算法处理的字符串模式可以转换为确定有限自动机(DFA)。
  • grep 工具使用的正则表达式可以转换为非确定有限自动机(NFA)。
  • javac 编译器将 Java 语言源代码编译为 Java 字节码。

7:小结

![L20-54RegularExpressions_55]

程序员:

  • 通过 DFA 模拟实现子串搜索功能。
  • 通过 NFA 模拟实现正则表达式模式匹配。

理论学者:

  • 正则表达式是描述一组字符串的紧凑表示方法。
  • NFA 是非确定性抽象机,其功能等价于正则表达式。
  • DFA、NFA 以及正则表达式都有其局限性。

你: 实际应用计算机科学的核心原理。

举例说明计算机科学中的关键范例:

  • 构建中间抽象层。
  • 挑选恰当的抽象模型!
  • 解决重要的实际问题。

(完)

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

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

相关文章

【经典算法】LeetCode101:对称二叉树(Java/C/Python3实现含注释说明,Easy)

对称二叉树 题目描述思路及实现方式一&#xff1a;递归&#xff08;推荐&#xff09;思路代码实现Java版本C语言版本Python3版本 复杂度分析 方式二&#xff1a;队列&#xff08;迭代&#xff09;思路代码实现Java版本C语言版本Python3版本 复杂度分析 总结相似题目 标签&#…

【XR806开发板试用】2、UDP控制的呼吸灯

【XR806开发板试用】1、UDP通信测试 上篇文章测试了XR806的UDP通信. 控制PWM控制相关的函数在device/xradio/xr806/adapter/hals/iot_hardware/wifiiot_lite文件夹下的iot_pwm.c . ├── BUILD.gn ├── iot_flash.c ├── iot_gpio.c ├── iot_i2c.c ├── iot_pwm.c ├…

车载以太网AVB交换机 TSN交换机 时间敏感网络 11口 千兆 SW2000TSN

目录 一、TSN时间敏感交换机概述 二、产品介绍 SW2000M/H TSN 1、产品框架 2、产品特点与参数 产品特点 产品参数 3、配置与使用 4、常用连接方式 4.1 双通道作为监控和数据采集器&#xff0c;采集两个设备间的通信数据&#xff08;Bypass功能&#xff09; 4.2 试验搭…

[中级]软考_软件设计_计算机组成与体系结构_05_CISC与RISC

CISC与RISC CISC&RISC的基本概念对比的维度往年真题 CISC&RISC的基本概念 对比的维度 CISC:指令多&#xff0c;使用频率差别大&#xff0c;变长格式&#xff0c;多种寻址方式RISC:指令少&#xff0c;使用频率接近&#xff0c;定长格式&#xff0c;多为寄存器寻址&#…

不锈钢潜水排污泵80WQP40-15-3S

一、产品概述&#xff1a;不锈钢潜水排污泵80WQP40-15-3S是一款专门设计用于污水排放的高性能潜水电泵。该泵采用了高级不锈钢材质&#xff0c;确保了泵体的耐腐蚀性和耐久性&#xff0c;非常适合在恶劣的工作环境中运行。其型号中的“80”指的是泵的出口为80mm&#xff0c;“W…

【竞技宝jjb.lol】LOL:LPL春季常规赛荣誉评选出炉!

北京时间2024年4月3日,英雄联盟LPL2024春季季后赛正在如火如荼的进行之中,常规赛阶段的荣誉评选结果在近日出炉,除三个最佳阵容之外,其中BLG战队的中单选手knight斩获春季赛常规赛MVP,而FPX战队的打野选手milkway则拿到春季赛常规赛的最佳新秀。 春季常规赛最有价值选手:BLG.kn…

Redis 全景图(1)--- 关于 Redis 的6大模块

这是我第一次尝试以长文的形式写一篇 Redis 的总结文章。这篇文章我想写很久了&#xff0c;只是一直碍于我对 Redis 的掌握没有那么的好&#xff0c;因此迟迟未动笔。这几天&#xff0c;我一直在看各种不同类型的 Redis 文章&#xff0c;通过阅读这些文章&#xff0c;引发了我对…

nestjs 全栈进阶--控制器和参数获取

视频教程 06_nest控制器和参数获取1_哔哩哔哩_bilibili nest new argument -p pnpm nest g resource person pnpm start:dev 测试下&#xff1a;http://localhost:3000/person/1 在浏览器中看到图中的内容就是成功了 1. 路由 在Nest.js中&#xff0c;路由是由Controller装…

CTF比赛中JWT漏洞的利用

前言 在最近的ctf比赛中&#xff0c;经常可以碰到一些jwt相关的题目&#xff0c;然后感觉思路挺有意思&#xff0c;拿出来分享一下&#xff0c;后边也总结一下ctf比较常见的集jwt相关题目解题思路 算法混淆漏洞 腾龙杯 web[这又是一个登录页面] 使用zkaq/zkaq登录之后&#…

在编程中使用中文到底该不该??

看到知乎上有个热门问题&#xff0c;为什么很多人反对中文在编程中的使用&#xff1f; 这个问题有几百万的浏览热度&#xff0c;其中排名第一的回答非常简洁&#xff0c;我深以为然&#xff1a; 在国内做开发&#xff0c;用中文写注释、写文档&#xff0c;是非常好的习惯&…

【C+ +初阶】前言篇章---C+ +的广袤

目录 1.C语言到C的过渡 2.C的发展历程 2.1C语言的诞生 2.2 c的历史版本 3.c 的地位 4. c的应用场景 4.1. 操作系统以及大型系统软件开发 所有操作系统几乎都是C/C写的 4.2. 服务器端开发 后台开发&#xff1a; 4.3. 游戏开发 4.4. 嵌入式 4.5. 数字图像处理 4.6. 人工智能 4.7.…

pygame--坦克大战(二)

加载敌方坦克 敌方坦克的方向是随机的&#xff0c;使用随机数生成。 初始化敌方坦克。 class EnemyTank(Tank):def __init__(self,left,top,speed):self.images {U: pygame.image.load(img/enemy1U.gif),D: pygame.image.load(img/enemy1D.gif),L: pygame.image.load(img/e…

IP地址获取不到的原因是什么?

在数字化时代的今天&#xff0c;互联网已成为我们日常生活和工作中不可或缺的一部分。而IP地址&#xff0c;作为互联网通信的基础&#xff0c;其重要性不言而喻。然而&#xff0c;有时我们可能会遇到IP地址获取不到的问题&#xff0c;这会给我们的网络使用带来诸多不便。那么&a…

基于单片机的光伏电量检测系统的设计

**单片机设计介绍&#xff0c;基于单片机的光伏电量检测系统的设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机的光伏电量检测系统的设计概要主要围绕实现光伏电量的实时监测与精准测量展开。以下是该设计的主要内…

flutter获取手机中的系统路径信息

https://www.bilibili.com/video/BV1wE421g7sw获取系统中的路径 获取系统中的路径&#xff0c;并在这个路径中创建一个文本文件【str.txt】 然后进行写入【str.txt】 再读取这个文件【str.txt】 手机没有开通root权限无法看到写入到【应用程序文档目录】路径中的文件 用来…

大创项目推荐 深度学习 python opencv 火焰检测识别 火灾检测

文章目录 0 前言1 基于YOLO的火焰检测与识别2 课题背景3 卷积神经网络3.1 卷积层3.2 池化层3.3 激活函数&#xff1a;3.4 全连接层3.5 使用tensorflow中keras模块实现卷积神经网络 4 YOLOV54.1 网络架构图4.2 输入端4.3 基准网络4.4 Neck网络4.5 Head输出层 5 数据集准备5.1 数…

Node.js介绍

Node.js 是一个开源和跨平台的 JavaScript 运行时环境。它是几乎任何类型的项目的流行工具&#xff01;

剑指Offer题目笔记25(使用回溯法解决其他类型问题)

面试题85&#xff1a; 问题&#xff1a; ​ 输入一个正整数n&#xff0c;输出所有包含n个左括号和n个右括号的组合&#xff0c;要求每个组合的左括号和右括号匹配。 解决方案&#xff1a; ​ 使用回溯法。因为要生成n个左括号和n个右括号&#xff0c;故需要走2n步&#xff0…

LabVIEW专栏三、探针和断点

探针和断点是LabVIEW调试的常用手段&#xff0c;该节以上一节的"测试耗时"为例 探针可以打在有线条的任何地方&#xff0c;打上后&#xff0c;经过这条线的所有最后一次的数值都会显示在探针窗口。断点可以打在程序框图的所有G代码对象&#xff0c;包括结构&#xf…

浅谈通信校验码及 CRC 校验

一、信息论中的 CRC 我上大学的时候,有一门课程叫做信息论,我就是从这个课程中学到的 CRC 校验这个词的,没错,当时学完整个课程后,CRC 对我来说依然只是一个单薄的缩写词语,全称我都不知道是啥。 CRC 全称是循环冗余校验(Cyclic Redundancy Check)。 说到信息论中的…