热门标签 | HotTags
当前位置:  开发笔记 > 人工智能 > 正文

KMP算法在词法分析中的应用

最近在学习编译原理,发现以前在数据结构中学习到的KMP算法在词法分析中使用到,词法分析中要识别词法单元,构建符号表,并将识别的词法单元返回给语法分析器来处理。在这个过程中有一个状态转化的过程,如下:

最近在学习编译原理,发现以前在数据结构中学习到的KMP算法在词法分析中使用到,词法分析中要识别词法单元,构建符号表,并将识别的词法单元返回给语法分析器来处理。在这个过程中有一个状态转化的过程,如下:

如要识别ababaa,状态转换图如下所示:

KMP提出了一种在文本串中搜索单个关键字b1b2....bn的算法,为了快速处理文本串并在这些串中搜索一个关键字,针对关键字b1b2....bn以及该关键字中的位置s(s表示关键字b1b2....bn中的状态转换图中的状态)定义了一个失效函数f(s),f(s)的含义如下:

f(s)表示匹配到状态s,但是匹配不到状态s+1,下一次从状态f(s)开始匹配。比如文本串ababcdcd,关键子ababaa,abab能匹配,所以f(4)表示匹配到状态4,但是状态4到状态5过不去了,因为无法匹配a,但是由于对于abab这个关键字子串,ab是abab的最长的真前缀同时也是abab的后缀,所以下次对于文本串中c只要与bf(4),即b2,进行比较即可,无需从头开始比较。

如何证得以上结论呢?

假设有一文本串为a1a2a3a4a5a6a7a8a9,关键字b1b2b3b4b5,并且a1a2a3a4与b1b2b3b4匹配,但是a5和b5不匹配,那么常规情况下我们会从头将文本串第一个字符与关键字的第一个字符进行比较,并继续第二个字符,但是因为a1a2a3a4与b1b2b3b4匹配,并且假设b1b2与b3b4也匹配,且是最长的既是b1b2b3b4的真前缀又是b1b2b3b4的后缀的子串,那么我们就可以跳过a2与b1的比较,直接让a5和b3进行比较,为什么呢?首先因为a3a4必然与b1b2匹配,所以跳过这两次匹配,其次假设你能找到一个a2a3a4a5a6与b1b2b3b4b5匹配,必然能得到a2a3a4等于b1b2b3,又因为a1a2a3等于b1b2b3,所以b1b2b3等于b2b3b4,这与假设b1b2是最长的既是b1b2b3b4的真前缀又是b1b2b3b4的后缀的子串相矛盾,所以上述KMP算法成立。

由上可知,f(s)函数的目标是使得b1b2....bf(s)是最长的既是b1b2....bs的真前缀又是b1b2....bs的后缀的子串。那么又如何针对一个关键字求得它的f(s)呢?

假设针对abababaab,首先我们看它的f(s)值如下:

s

1

2

3

4

5

6

7

8

9

f(s)

 0

 0

 1

 2

 3

 4

 5

 1

 2

如果我们针对f(7),求它的值该如何求呢?上述关键字的状态转换图如下:

假设我们已经知道f(1)到f(6)的值,因为f(6)的值等于4,可得知b1b2b3b4等于b3b4b5b6,如果b5等于b7,那么也就是说b1b2b3b4b5等于b3b4b5b6b7,则f(7)=f(6)+1=5,上述关键字成立。

如果我们针对f(8),求它的值该如何求呢?

如果b6等于b8,则f(8)=f(7)+1,但是b6=b,b8=a,不想等,所以f(8)不能这么求,那么又该如何求呢?因为f(8)的值必然落在1到5之间,那么f(8)和f(5)的关系又如何呢?因为f(5)的值为3,则可推出b1b2b3= b3b4b5,且b1b2b3b4b5等于b3b4b5b6b7,,又因为b3b4b5 = b5b6b7,所以b1b2b3 = b5b6b7,所以只要判断b4与b8的值是否相同即可,若相同,则f(8)=f(5)+1,但是b4 = b与b8 = a,不相同,所以需要继续往下推断f(8)与f(3)的关系?因为b1b2b3 = b3b4b5 ,判断b2的值是否等于b8,不相同,最终若判断b1的值是否与b8的值相同,相同,所以f(8)的值为1,否则为0。

关于f(s)的算法如下:

t = 0;
f(1) = 0;
for(s = 1; s )
{
    while(t>0 && bs+1 != bt+1)  t = f(t);
    if(b
s+1
 == b
t+1
)  //如果不是t=0跳出来的
    {
        t = t+1;
        f(s+1) = t;
    }
    else f(s+1) = 0;  //如果t=0跳出来的
}

 

 

 

 


推荐阅读
  • 本文详细介绍了Java编程语言中的核心概念和常见面试问题,包括集合类、数据结构、线程处理、Java虚拟机(JVM)、HTTP协议以及Git操作等方面的内容。通过深入分析每个主题,帮助读者更好地理解Java的关键特性和最佳实践。 ... [详细]
  • 本文将介绍如何使用 Go 语言编写和运行一个简单的“Hello, World!”程序。内容涵盖开发环境配置、代码结构解析及执行步骤。 ... [详细]
  • 1:有如下一段程序:packagea.b.c;publicclassTest{privatestaticinti0;publicintgetNext(){return ... [详细]
  • 360SRC安全应急响应:从漏洞提交到修复的全过程
    本文详细介绍了360SRC平台处理一起关键安全事件的过程,涵盖从漏洞提交、验证、排查到最终修复的各个环节。通过这一案例,展示了360在安全应急响应方面的专业能力和严谨态度。 ... [详细]
  • 本文深入探讨了Linux系统中网卡绑定(bonding)的七种工作模式。网卡绑定技术通过将多个物理网卡组合成一个逻辑网卡,实现网络冗余、带宽聚合和负载均衡,在生产环境中广泛应用。文章详细介绍了每种模式的特点、适用场景及配置方法。 ... [详细]
  • 本文详细介绍了macOS系统的核心组件,包括如何管理其安全特性——系统完整性保护(SIP),并探讨了不同版本的更新亮点。对于使用macOS系统的用户来说,了解这些信息有助于更好地管理和优化系统性能。 ... [详细]
  • 本文探讨了 C++ 中普通数组和标准库类型 vector 的初始化方法。普通数组具有固定长度,而 vector 是一种可扩展的容器,允许动态调整大小。文章详细介绍了不同初始化方式及其应用场景,并提供了代码示例以加深理解。 ... [详细]
  • libsodium 1.0.15 发布:引入重大不兼容更新
    最新发布的 libsodium 1.0.15 版本带来了若干不兼容的变更,其中包括默认密码散列算法的更改和其他重要调整。 ... [详细]
  • PHP 编程疑难解析与知识点汇总
    本文详细解答了 PHP 编程中的常见问题,并提供了丰富的代码示例和解决方案,帮助开发者更好地理解和应用 PHP 知识。 ... [详细]
  • SQL中UPDATE SET FROM语句的使用方法及应用场景
    本文详细介绍了SQL中UPDATE SET FROM语句的使用方法,通过具体示例展示了如何利用该语句高效地更新多表关联数据。适合数据库管理员和开发人员参考。 ... [详细]
  • 技术分享:从动态网站提取站点密钥的解决方案
    本文探讨了如何从动态网站中提取站点密钥,特别是针对验证码(reCAPTCHA)的处理方法。通过结合Selenium和requests库,提供了详细的代码示例和优化建议。 ... [详细]
  • CATSearch是一个针对CATIA V5和3DEXPERIENCE平台的开源二次开发项目,由硬核小青年发起并维护。该项目旨在解决3DE搜索功能不稳定的问题,通过API调用提供更快速、准确的搜索体验。本文将详细介绍该插件的功能及使用方法。 ... [详细]
  • 深入理解Java泛型:JDK 5的新特性
    本文详细介绍了Java泛型的概念及其在JDK 5中的应用,通过具体代码示例解释了泛型的引入、作用和优势。同时,探讨了泛型类、泛型方法和泛型接口的实现,并深入讲解了通配符的使用。 ... [详细]
  • 本文介绍如何在Java项目中使用Log4j库进行日志记录。我们将详细说明Log4j库的引入、配置及简单应用,帮助开发者快速上手。 ... [详细]
  • 本文详细记录了在银河麒麟操作系统和龙芯架构上使用 Qt 5.15.2 进行项目打包时遇到的问题及解决方案,特别关注于 linuxdeployqt 工具的应用。 ... [详细]
author-avatar
隔壁老吴
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有