【NLP】有限自动机的KMP算法

news2024/11/28 14:15:47

目录

一、说明

二、无策略直接匹配法

2.1  简单粗暴的无脑匹配:

2.2 跳过外循环的思路

2.3 跳过内循环的思路

2.4 KMP算法时间分析

三、KMP有限状态机

四、结论


一、说明

        KMP算法问题:给定一个(短)模式和一个(长)文本,这两个都是字符串,确定该模式是否出现在文本中的某处。上次我们看到了如何使用有限自动机来做到这一点。这次我们将介绍 Knuth-Morris-Pratt (KMP) 算法,它可以被认为是构建这些自动机的有效方法。

Knuth-Morris-Pratt 字符串匹配:见【NLP】KMP匹配算法_无水先生的博客-CSDN博客

二、无策略直接匹配法

        首先让我们看一个没策略的简单的解决方案。

  • 假设文本在一个数组中:char T[n]
  • 模式在另一个数组中:char P[m]。

        一种简单的方法就是尝试该模式可能出现在文本中的每个可能位置。

2.1  简单粗暴的无脑匹配:

    for (i=0; T[i] != '\0'; i++)
    {
            for (j=0; T[i+j] != '\0' && P[j] != '\0' && T[i+j]==P[j]; j++) ;
            if (P[j] == '\0') found a match
    }

        有两个嵌套循环;内部的需要 O(m) 次迭代,外部的需要 O(n) 次迭代,所以总时间是乘积 O(mn)。这很慢;我们想加快速度。

        在实践中,这工作得很好——通常不像这个 O(mn) 最坏情况分析那么糟糕。这是因为内层循环通常会很快发现不匹配并移动到下一个位置,而无需经过所有 m 个步骤。但是这种方法对于某些输入仍然可以采用 O(mn)。在一个不好的例子中,T[] 中的所有字符都是“a”,而 P[] 除了末尾的一个“b”之外都是“a”。然后每次都要进行 m 次比较才能发现你没有匹配,所以总的来说是 mn 次。

        这是一个更典型的例子。每行代表外循环的一次迭代,行中的每个字符代表比较的结果(如果比较不相等则为 X)。假设我们要在文本“banananobano”中寻找模式“nano”。

     0  1  2  3  4  5  6  7  8  9 10 11
      T: b  a  n  a  n  a  n  o  b  a  n  o

    i=0: X
    i=1:    X
    i=2:       n  a  n  X
    i=3:          X
    i=4:             n  a  n  o
    i=5:                X
    i=6:                   n  X
    i=7:                         X
    i=8:                            X
    i=9:                               n  X
    i=10:                                 X

        其中一些比较是无用功!例如,在迭代 i=2 之后,我们从已经完成的比较中得知 T[3]="a",因此在迭代 i=3 中将它与 "n" 进行比较是没有意义的。而且我们也知道 T[4]="n",所以在迭代 i=4 中进行相同的比较是没有意义的。

2.2 跳过外循环的思路

        Knuth-Morris-Pratt 的想法是,在这种情况下,在你投入大量工作对代码的内部循环进行比较之后,你就会对文本中的内容了解很多。具体来说,如果您找到了从位置 i 开始的 j 个字符的部分匹配,您就会知道位置 T[i]...T[i+j-1] 中的内容。

        您可以使用这些知识以两种方式节省工作。首先,您可以跳过一些无法匹配的迭代。尝试将找到的部分匹配项与要查找的新匹配项重叠:

    i=2: n  a  n
    i=3:    n  a  n  o

        这里模式的两个位置相互冲突——我们从 i=2 迭代中知道 T[3] 和 T[4] 是“a”和“n”,所以它们不可能是“n”和 i=3 迭代正在寻找的“a”。我们可以不断跳过位置,直到找到一个不冲突的位置:

    i=2: n  a  n
    i=4:       n  a  n  o

        这里两个“n”是重合的。将两个字符串 x 和 y 的重叠定义为作为 x 的后缀和 y 的前缀的最长单词。这里“nan”和“nano”的重叠只是“n”。 (我们不允许重叠全部是 x 或 y,所以它不是“nan”)。通常,我们要跳转到的 i 的值是与当前部分匹配的最大重叠对应的值:

        具有跳过迭代的字符串匹配:

    i=0;
    while (i<n)
    {
    for (j=0; T[i+j] != '\0' && P[j] != '\0' && T[i+j]==P[j]; j++) ;
    if (P[j] == '\0') found a match;
    i = i + max(1, j-overlap(P[0..j-1],P[0..m]));
    }

2.3 跳过内循环的思路

        一个可以做的优化是跳过内循环中的一些迭代。让我们看一下同一个例子,其中我们从 i=2 跳到 i=4:

    i=2: n  a  n
    i=4:       n  a  n  o

        在此示例中,重叠的“n”已通过 i=2 迭代进行了测试。无需在 i=4 迭代中再次测试它。一般来说,如果我们与最后的部分匹配有重要的重叠,我们可以避免测试与重叠长度相等的字符数。

        此更改会产生(一个新版本的)KMP 算法:

KMP, version 1:

    i=0;
    o=0;
    while (i<n)
    {
    for (j=o; T[i+j] != '\0' && P[j] != '\0' && T[i+j]==P[j]; j++) ;
    if (P[j] == '\0') found a match;
    o = overlap(P[0..j-1],P[0..m]);
    i = i + max(1, j-o);
    }

        唯一剩下的细节是如何计算重叠函数。这只是 j 的函数,而不是 T[] 中字符的函数,因此我们可以在进入算法的这一部分之前在预处理阶段计算一次。首先让我们看看这个算法有多快。

2.4 KMP算法时间分析

        我们还有一个外循环和一个内循环,所以看起来时间可能仍然是 O(mn)。但是我们可以用不同的方式来计算它,看看它实际上总是小于那个。这个想法是,每次通过内循环,我们都做一次比较 T[i+j]==P[j]。我们可以通过计算我们执行了多少次比较来计算算法的总时间。

        我们将比较分为两组:返回 true 的和返回 false 的。如果比较返回真,我们就确定了 T[i+j] 的值。然后在未来的迭代中,只要存在涉及 T[i+j] 的非平凡重叠,我们就会跳过该重叠并且不再与该位置进行比较。所以T[]的每个位置只涉及一次真正的比较,总共可以有n次这样的比较。另一方面,外循环的每次迭代最多有一个错误比较,因此也只能有 n 个。结果我们看到 KMP 算法的这一部分最多进行 2n 次比较并且花费时间 O(n)。

三、KMP有限状态机

        如果我们只看上面算法中 j 发生了什么,它有点像有限自动机。在每个步骤中,j 被设置为 j+1(在内部循环中,在匹配之后)或重叠 o(在不匹配之后)。在每一步中,o 的值只是 j 的函数,并不依赖于其他信息,例如 T[] 中的字符。所以我们可以画出类似自动机的东西,用箭头连接 j 的值并用匹配和不匹配标记。

        这和我们习惯的自动机之间的区别在于它每个圆圈只有两个箭头,而不是每个字符一个。但是我们仍然可以像任何其他自动机一样模拟它,方法是在开始状态 (j=0) 上放置一个标记并围绕箭头移动它。每当我们在 T[] 中找到一个匹配的字符时,我们就会继续处理文本的下一个字符。但是每当我们遇到不匹配时,我们在下一步中查看相同的字符,除了状态 j = 0 中的不匹配情况。

        所以在这个例子中(与上面的例子相同)自动机经历了状态序列:

    j=0
            mismatch T[0] != "n"
    j=0
            mismatch T[1] != "n"
    j=0
            match T[2] == "n"
    j=1
            match T[3] == "a"
    j=2
            match T[4] == "n"
    j=3
            mismatch T[5] != "o"
    j=1
            match T[5] == "a"
    j=2
            match T[6] == "n"
    j=3
            match T[7] == "o"
    j=4
            found match
    j=0
            mismatch T[8] != "n"
    j=0
            mismatch T[9] != "n"
    j=0
            match T[10] == "n"
    j=1
            mismatch T[11] != "a"
    j=0
            mismatch T[11] != "n"

        这基本上与上面的 KMP 伪代码完成的比较序列相同。所以这个自动机提供了 KMP 算法的等价定义。

        有个疑问点,该自动机中可能不清楚的一个转换是从 j=4 到 j=0 的转换。一般来说,应该有从 j=m 到某个较小的 j 值的转换,这应该发生在任何字符上(在进行此转换之前没有更多的匹配项要测试)。如果我们想找到模式的所有出现,我们应该能够找到一个出现,即使它与另一个重叠。因此,例如,如果模式是“nana”,我们应该在文本“nanana”中找到它的两次出现。因此,从 j=m 的过渡应该转到下一个可以匹配的最长位置,即 j=overlap(pattern,pattern)。在这种情况下,overlap("nano","nano") 为空(“nano”的所有后缀都使用字母“o”,没有前缀)所以我们转到 j=0。

四、结论

        基于自动机模型,将模式一次性扫描自动构成自动机模型,这是有意义的。如果给出一个模式,设计一套自动机,这种实用性不足。看来研究一个随生成限状态机是必要的。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

PCB材料选择与性能比较

PCB板被广泛应用于电子行业&#xff0c;作为电子设备的重要组成部分之一&#xff0c;负责连接各种电子元件。PCB板的性能直接影响着电子设备的质量和稳定性。而PCB板的材料选择则是影响PCB板性能的关键因素之一。本文将对常见PCB材料进行比较分析&#xff0c;以便于选择适合的材…

西电网课UMOOCs《英美概况》1-15单元课后答案

声明&#xff1a;本文CSDN作者原创投稿文章&#xff0c;未经许可禁止任何形式的转载&#xff0c;原文链接 如果图片挂了&#xff0c;可以移步至我的博客西电网课UMOOCs《英美概况》1-15单元课后答案 - 小木槌 文章目录 Quiz for Unit 1Quiz for Unit 2Quiz for Unit 3Quiz for…

C/C++基础补充

1. NULL和nullptr 有如下代码&#xff1a; void func(int a) {cout << "func(int)" << endl; }void func(int* p) {cout << "func(int*)" << endl; }void test() {func(0); // func(int);func(NULL); // func(int);fun…

带你探索400G光模块测试

随着移动互联网、云计算、大数据等技术快速发展&#xff0c;数据中心及云计算资源需求的爆发式地增长&#xff0c;核心网传输带宽需求大幅度的提升&#xff0c;同时也带动了超大规模云数据中心的发展&#xff0c;对数据中心内部和之间的互联的光模块带宽需求呈快速增长&#xf…

ChatGPT 使用 拓展资料:吴恩达大咖 Building Systems with the ChatGPT API 思维链

ChatGPT 使用 拓展资料:吴恩达大咖 Building Systems with the ChatGPT API 思维链 在本节中,我们将重点讨论要处理输出的任务,这些任务通常通过一系列步骤来获取输入并生成有用的输出。有时,在回答特定问题之前,模型详细推理问题是很重要的。如果你参加了我们之前为开发人…

项目管理软件大对比:2023年15款最佳项目管理工具

简单的项目只需要一个电子表格清单可能就管理好了&#xff0c;而复杂的项目则需要适当的规划、任务分配、设定截止日期&#xff0c;以确保每个人都遵守它们、大家进行紧密的协作&#xff0c;并追踪所花费的时间。 让项目量化、可视化&#xff0c;资源合理分配、更容易的协作和…

x265的DCT

文章目录 DCT相关背景知识DCT变换系数矩阵32x32变换矩阵系数其他尺寸变换矩阵系数 变换计算过程流程图 代码实现数据残差变换系数对应残差 我的简单实现实现细节实现代码 x265对应代码实现openHEVC代码实现 DCT相关背景知识 DCT变换系数矩阵 标准提供了32x32的系数矩阵&#…

Leetcode | 39 组合总和

Leetcode | 39 组合总和 题目 给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target &#xff0c;找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 &#xff0c;并以列表形式返回。你可以按 任意顺序 返回这些组合。 candidates 中的 同一个 数…

理解 Vue 中的 MVVM 思想

1. 什么是 Vue Vue 是一套用于构建用户界面的渐进式 JavaScript 框架, 与其他大型框架不同的是, Vue 被设计为可以自底向上逐层应用, Vue 的核心库只关心视图层, 方便与第三方库或既有项目整合. 2. JavaScript 框架了解 jQuery : 大家熟悉的 JavaScript 框架, 优点是简化了 D…

VulnHub项目:MONEY HEIST: 1.0.1

靶机地址&#xff1a;Money Heist: 1.0.1 ~ VulnHub 渗透过程&#xff1a; 确定靶机ip&#xff0c;攻击机kali的ip 对靶机进行端口检测 存在22、53、80、3000、3001端口&#xff0c;访问80端口 发现了登录注册按钮&#xff0c;尝试进行注册 注册成功后进行登录&#xff0c…

VulnHub项目:Gaara

项目地址&#xff1a;Gaara: 1 ~ VulnHub 我爱罗&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;火影前200集无敌存在&#xff01;&#xff01;&#xff01; 渗透过程&#xff1a; 收集三件套&#xff01;搞一手~&#xff0c;发现80&#xff0c;访问web&…

第四节 ogre 2.3实现一个简单的模型纹理贴图

本节简单介绍下如何使用Ogre 2.3加载模型&#xff0c;并给模型贴上纹理材质。 一. 安装ogre 2.3 主要有两种安装方法&#xff1a; 简单安装方法&#xff0c;使用scripts for Ogre 2.3 脚本,按照官网给出的步骤安装即可。需要注意的是脚本解压后的 *.bat 文件需要修改下 CMAK…

【Java|golang】2611. 老鼠和奶酪

有两只老鼠和 n 块不同类型的奶酪&#xff0c;每块奶酪都只能被其中一只老鼠吃掉。 下标为 i 处的奶酪被吃掉的得分为&#xff1a; 如果第一只老鼠吃掉&#xff0c;则得分为 reward1[i] 。 如果第二只老鼠吃掉&#xff0c;则得分为 reward2[i] 。 给你一个正整数数组 reward1…

SpringCloud-Gateway过滤器

路由过滤器 GatewayFilter GatewayFilter 是网关中提供的一种过滤器&#xff0c;可以对进入网关的请求和微服务返回的响应做处理。 路由过滤器的作用是什么&#xff1f; 对路由的请求或想象做加工处理&#xff0c;比如添加请求头配置子路由下的过滤器只对当前路由的请求生效…

monkey测试关机/重启问题分析(二)

systemui关机dialog相关 1、systemui下拉关机按钮 通过Android 布局分析工具发现 按钮布局 base/packages/SystemUI/res-keyguard/layout/footer_actions.xml 按钮初始化和点击事件 frameworks/base/packages/SystemUI/src/com/android/systemui/qs/FooterActionsControlle…

斐波那契算法的理解

1.斐波那契数列 &#xff1a; 数组&#xff1a;int[] F{1, 1, 2, 3, 5, 8, 13, 21, 34, 55 }; 特点&#xff1a; 从第三个数开始&#xff0c;后边每一个数都是前两个数的和 。F[k]F[k-1]F[k-2]; 如图所示&#xff1a; ①low、mid、high都是F数组的索引&#xff0c;F[k]-1表示…

基础实验篇 | 课程总体介绍(一)

本讲主要介绍多旋翼的特点及选用多旋翼作为实验平台的原因、对于无人系统教育的一些新需求、RflySim平台对于飞控的底层控制算法的开发优势、本期平台课程的设置、以及如何开发自驾仪系统。 相较于固定翼和直升机&#xff0c;多旋翼具有机械结构简单、 易维护的优点。以四旋翼…

操作Arrays.asList的list报UnsupportedOperationException的坑

Arrays.asList() 将数组转换成List集合 /*** Returns a fixed-size list backed by the specified array. (Changes to* the returned list "write through" to the array.) This method acts* as bridge between array-based and collection-based APIs, in* com…

通过Python封装商品ID获取阿里巴巴商品详情数据,阿里巴巴商品详情数据API接口,阿里巴巴API接口

目的&#xff1a;通过Python封装商品ID获取阿里巴巴商品详情数据&#xff0c;本文将给出Python代码的一些思路和示例。 首先&#xff0c;你需要找到获取阿里巴巴商品详情数据的API接口。阿里巴巴开放平台提供了一些API接口&#xff0c;例如阿里巴巴开放平台商品API&#xff0c…

软件开发项目成本控制的7个重点

1、精细计划预算和管控机制 制定详细的项目计划和预算&#xff0c;包括资源需求、人力资源、时间表和财务预测等&#xff0c;以确保项目不会超出预算。实时跟踪项目的实际开支和进度&#xff0c;并对计划进行调整&#xff0c;以便更好地管理成本。 软件开发项目成本控制的7个重…