24考研数据结构复习(6)——串的模式匹配

本系列文章是笔者复习自用,参考教材为严蔚敏编著的《数据结构(C语言版)》,如能对大家有所帮助实属荣幸

本篇的内容主要是介绍串的模式匹配和KMP算法,并介绍KMP算法中next数组的手算方法。

6.1 串的相关概念

串是由零个或多个字符组成的有限序列,一般记为

S='a_{1}a_{2}a_{3}...a_{n}'

其中,S是串名,单引号括起来的是串的值,字符的个数n称为串的长度,n=0的串被称为空串。

串中任意连续的字符组成的子序列称为该串的子串,某个字符在串中的序号称为该字符在串中的位置,子串在主串中的位置以子串的第一个字符在主串中的位置表示。

例如,串A=‘China BeiJing’ ,B='China',C='BeiJing',那么B和C都是A的子串,且B在A中的位置是1,C在A中的位置是7。

介绍完串的概念之后,我们就可以进入模式匹配算法的学习了,由于研究生考试对串的存储结构没有要求,所以我们不介绍串的定义和相关函数。

6.2 模式匹配算法

1.暴力算法

子串的定位操作通常称为串的模式匹配,求的是子串在主串的位置。在学习KMP之前,我们通常使用一种简单暴力算法,即从主串的开头开始遍历,寻找与模式串中第一个字符相匹配的字符,找到之后,再看主串中的下一个字符与模式串中下一个字符是否匹配,以此类推,若发现不匹配项,则返回主串上次开始匹配的字符的下一位重新寻找匹配,如下图所示:

注意,在这里我将 至少有一个字符相同 的情况算作一次匹配。

仔细观察之后会发现,有很多比较其实不必要做,比如在第一次匹配失败之后,完全可以从主串第三位开始比较;在第二次匹配失败之后,可以从主串第六位开始比较。按照这个思路,我们对上面的过程进行优化,得到下图::

依照上图,我们可以发现节省了很多步骤,这就是KMP算法的思路。

2.KMP算法

在暴力匹配中,每趟匹配失败都是模式后移一位再从头开始比较。而某趟已匹配相等的字符序列是模式的某个前缀,这就相当于模式串在不断自我比较,导致低效率。因此,我们可以从分析模式本身的结构着手,如果已匹配相等的前缀序列中有某个后缀正好是模式的前缀,那么就可以将模式向后滑动到与这些相等字符对齐的位置,而主串指针无需回溯,并从该位置开始继续比较。

KMP的相关概念:

前缀:除了最后一个字符以外,字符串所有的头部子串。

后缀:除了第一个字符以外,字符串所有的尾部子串。

部分匹配值:字符串的前缀和后缀的最长相等前后缀长度。

以‘ababa’为例:

‘a’无前缀后缀,所以最大共同前后缀为0。

'ab'的前缀为{'a'},后缀为{'b'},所以最大共同前后缀为0。

'aba'的前缀为{‘a’,'ab'},后缀为{'ba','a'},有共同前后缀a,所以最大共同前后缀为1。

‘abab’的前缀为{'a','ab','aba'},后缀为{'bab','ab','b'},有共同前后缀ab,所以最大共同前后缀为2。

'ababa'的前缀为{'a','ab','aba','abab'},后缀为{'baba','aba','ba','a'},有共同前后缀aba,所以最大共同前后缀为3。

下面我们来谈谈最大共同前后缀的作用

这里我们以ABABAAABABAA为例:

如果第一位就不匹配,那就只能用子串第一位和主串的下一位进行比较,相当于用子串第0位与当当前位进行比较,取0。

如果第二位发生不匹配,取第二位之前的部分,‘A’,最大前后缀长度为0,则需要用子串第一位与主串当前位进行比较,取1。

如果第三位发生不匹配,取第三位之前的部分,‘AB’,最大前后缀长度为0,则需要用子串第一位与主串当前位进行比较,取1。

如果第四位发生不匹配,取第四位之前的部分,‘ABA’,最大前后缀长度为1,则需要用子串第二位与主串当前位进行比较,取2。

如果第五位发生不匹配,取第五位之前的部分,‘ABAB’,最大前后缀长度为2,则需要用子串第三位与主串当前位进行比较,取3。

如果第六位发生不匹配,取第六位之前的部分,‘ABABA’,最大前后缀长度为3,则需要用子串第四位与主串当前位进行比较,取4。

如果第七位发生不匹配,取第七位之前的部分,‘ABABAA’,最大前后缀长度为1,则需要用子串第二位与主串当前位进行比较,取2。

如果第八位发生不匹配,取第八位之前的部分,‘ABABAAA’,最大前后缀长度为1,则需要用子串第二位与主串当前位进行比较,取2。

如果第九位发生不匹配,取第九位之前的部分,‘ABABAAAB’,最大前后缀长度为2,则需要用子串第三位与主串当前位进行比较,取3。

如果第十位发生不匹配,取第十位之前的部分,‘ABABAAABA’,最大前后缀长度为3,则需要用子串第四位与主串当前位进行比较,取4。

如果第十一位发生不匹配,取第十一位之前的部分,‘ABABAAABAB’,最大前后缀长度为4,则需要用子串第二位与主串当前位进行比较,取5。

如果第十二位发生不匹配,取第十二位之前的部分,‘ABABAAABABA’,最大前后缀长度为5,则需要用子串第二位与主串当前位进行比较,取6。

最终得出该模式串的next数组为:[0,1,1,2,3,4,2,2,3,4,5,6]。

基于这种思想,有一种简便的计算方法,将数组每一位对应模式串每一位,首先第一位默认取0,对于第一位之后的位数,截取该位之前的字符串,找到其最大前后缀长度,加一后就是数组中的值,举个例子。

设有字符串P='aabaac',求其next数组。

首先第一位取0,略过。

第二位,前面的字符串a,无最大前后缀,0+1=1。

第三位,前面的字符串aa,最大前后缀a,1+1=2。

第四位,前面的字符串aab,无最大前后缀,0+1=1。

第五位,前面的字符串aaba,最大前后缀a,1+1=2。

第六位,前面的字符串aabaa,最大前后缀aa,2+1=3。

所以,该next数组为[0,1,2,1,2,3]。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值