数据结构之KMP算法

一. 首先求next值

例如: 模式串 a b a a b c a c          

             next值 0 1 1 2 2 3 1 2

next数组的求解方法是:第一位的next值为0,第二位的next值为1,后面求解每一位的next值时,根据前一位进行比较。首先将前一位与其next值对应的内容进行比较,如果相等,则该位的next值就是前一位的next值加上1;如果不等,向前继续寻找next值对应的内容来与前一位进行比较,直到找到某个位上内容的next值对应的内容与前一位相等为止,则这个位对应的值加上1即为需求的next值;如果找到第一位都没有找到与前一位相等的内容,那么需求的位上的next值即为1。

1.前两位必定为0和1。

2.计算第三位的时候,看第二位b的next值,为1,则把b和1对应的a进行比较,不同,则第三位a的next的值为1,因为一直比到最前一位,都没有发生比较相同的现象。

3.计算第四位的时候,看第三位a的next值,为1,则把a和1对应的a进行比较,相同,则第四位a的next的值为第三位a的next值加上1。为2。因为是在第三位实现了其next值对应的值与第三位的值相同。

4.计算第五位的时候,看第四位a的next值,为2,则把a和2对应的b进行比较,不同,则再将b对应的next值1对应的a与第四位的a进行比较,相同,则第五位的next值为第二位b的next值加上1,为2。因为是在第二位实现了其next值对应的值与第四位的值相同。

5.计算第六位的时候,看第五位b的next值,为2,则把b和2对应的b进行比较,相同,则第六位c的next值为第五位b的next值加上1,为3,因为是在第五位实现了其next值对应的值与第五位相同。

6.计算第七位的时候,看第六位c的next值,为3,则把c和3对应的a进行比较,不同,则再把第3位a的next值1对应的a与第六位c比较,仍然不同,则第七位的next值为1。 7.计算第八位的时候,看第七位a的next值,为1,则把a和1对应的a进行比较,相同,则第八位c的next值为第七位a的next值加上1,为2,因为是在第七位和实现了其next值对应的值与第七位相同。

二. nextval值的求法

例如主串为“aaabaaaab”、  

        模式串为“aaaab”

在计算nextval之前要先弄明白,nextval是为了弥补next函数在某些情况下的缺陷而产生的。 例如主串为“aaabaaaab”、模式串为“aaaab”那么,比较的时候就会发生一些浪费的情况:比较到主串以及模式串的第四位时,发现其值并不相等,据我们观察,我们可以直接从主串的第五位开始与模式串进行比较,而事实上,却进行了几次多余的比较。使用nextval可以去除那些不必要的比较次数。

模式串       a b a a b c a c

next值       0 1 1 2 2 3 1 2

nextval值  0 1 0 2 1 3 0 2

1.第一位的nextval值必定为0,第二位如果于第一位相同则为0,如果不同则为1。  

2.第三位的next值为1,那么将第三位和第一位进行比较,均为a,相同,则,第三位的nextval值为0。

3.第四位的next值为2,那么将第四位和第二位进行比较,不同,则第四位的nextval值为其next值,为2。

4.第五位的next值为2,那么将第五位和第二位进行比较,相同,第二位的next值为1,则继续将第二位与第一位进行比较,不同,则第五位的nextval值为第二位的next值,为1。

5.第六位的next值为3,那么将第六位和第三位进行比较,不同,则第六位的nextval值为其next值,为3。

6.第七位的next值为1,那么将第七位和第一位进行比较,相同,则第七位的nextval值为0。

7.第八位的next值为2,那么将第八位和第二位进行比较,不同,则第八位的nextval值为其next值,为2。

三. KMP算法

  • KMP算法是用来处理字符串匹配的。换句话说,给你两个字符串,你需要回答,B串是否是A串的子串(A串是否包含B串)。例如”Today is Tuesday”.中是否包含”day”,在哪些位置包含。
  • 这个算法是由Knuth、Morris、Pratt三个提出来的,取了这三个人的名字的头一个字母。
  • 假如,A="abababaababacb",B="ababacb",我们来看看KMP是怎么工作的。我们用两个指针i和j分别表示,A[i-j+ 1..i]与B[1..j]完全相等。 也就是说,i是不断增加的,随着i的增加j相应地变化,且j满足以A[i]结尾的长度为j的字符串正好匹配B串的前 j个字符(j当然越大越好),现在需要检验A[i+1]和B[j+1]的关系。当A[i+1]=B[j+1]时,i和j各加一;什么时候j=m了,我们就说B是A的子串(B串已经整完了),并且可以根据这时的i值算出匹配的位置。
  • 当A[i+1]<>B[j+1],KMP的策略是调整j的位置(减小j值)使得A[i-j+1..i]与B[1..j]保持匹配且新的B[j+1]恰好与A[i+1]匹配(从而使得i和j能继续增加)。我们看一看当 i=j=5时的情况。位置。

          i: 1 2 3 4 5 6 7 8 9 10

         A: a b a b a b a a b a b …

          B: a b a b a c b

           j: 1 2 3 4 5 6 7 8 9 10

  • 从上面的这个例子,我们可以看到,新的j可以取多少与i无关,只与B串有关。我们完全可以预处理出这样一个数组P[j],表示当匹配到B数组的第j个字母而第j+1个字母不能匹配了时,新的j最大是多少。P[j]应该是所有满足B[1..P[j]]=B[j-P[j]+1..j]的最大值。
  • 事实上,有可能j到了0仍然不能满足A[i+1]=B[j+1](比如A[8]="d"时)。因此,准确的说法是,当j=0了时,我们增加i值但忽略j直到出现A[i]=B[1]为止。

算法实现:

最后的j:=P[j]是 为了让程序继续做 下去,因为我们有 可能找到多处匹配。 这个程序或许 比想像中的要简单, 因为对于i值的不断 增加,代码用的是for循环。因此,这个代码可以这样形象地理解:扫描字符串A,并更新可以匹配到B的什么位置。

 

 

 

 

KMP算法是一种字符串匹配法,用于在一个较长的字符串中查找一个较短的模式串。它通过构建next数组来实现快速匹配。 KMP算法的核心思想是在每次失配时,不是将模式串向后移动一位,而是将模式串向后移动至下一次可以和前面部分匹配的位置,从而跳过大部分的失配步骤。这样可以大大提高匹配效率。 next数组是部分匹配表,它存放的是每一个下标对应的部分匹配。部分匹配是指前缀和后缀的最长共有元素的长度。通过构建next数组,可以在匹配过程中根据当前失配的位置快速确定模式串的移动步数。 下面是手动模拟KMP算法的过程: 1. 首先,根据模式串构建next数组。假设模式串为p[],长度为m。初始化next数组,next = -1,next = 0。 2. 从第2个位置开始,依次计next[i]的。假设已经计到next[i-1],则比较p[i-1]和p[next[i-1]]的: - 如果p[i-1]等于p[next[i-1]],则next[i] = next[i-1] + 1; - 如果p[i-1]不等于p[next[i-1]],则将next[i-1]的赋给j,然后继续比较p[i-1]和p[j]的,直到找到一个满足p[i-1]等于p[j]的位置或者j等于0为止。此时,next[i] = j。 3. 完成next数组的构建后,开始匹配过程。假设待匹配的字符串为s[],长度为n。初始化i = 0,j = 0。 4. 依次比较s[i]和p[j]的: - 如果s[i]等于p[j],则i和j分别向后移动一位; - 如果s[i]不等于p[j],则根据next数组确定模式串的移动步数。将j移动到next[j]的位置,即j = next[j]。 5. 重复步骤4,直到匹配成功(j等于模式串的长度m)或者遍历完整个字符串s。 通过上述步骤,可以实现高效的字符串匹配。KMP算法的时间复杂度为O(n+m),其中n为字符串s的长度,m为模式串p的长度。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

流萤数点

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值