kmp算法的next[]数组通俗解释

baihaoxiang baihaoxiang     2022-10-09     166

关键词:

我们在一个母字符串中查找一个子字符串有很多方法。KMP是一种最常见的改进算法,它可以在匹配过程中失配的情况下,有效地多往后面跳几个字符,加快匹配速度。

当然我们可以看到这个算法针对的是子串有对称属性,如果有对称属性,那么就需要向前查找是否有可以再次匹配的内容。

 

在KMP算法中有个数组,叫做前缀数组,也有的叫next数组,每一个子串有一个固定的next数组,它记录着字符串匹配过程中失配情况下可以向前多跳几个字符,当然它描述的也是子串的对称程度,程度越高,值越大,当然之前可能出现再匹配的机会就更大。

这个next数组的求法是KMP算法的关键,但不是很好理解,我在这里用通俗的话解释一下,看到别的地方到处是数学公式推导,看得都蛋疼,这个篇文章仅贡献给不喜欢看数学公式又想理解KMP算法的同学。

 

1、用一个例子来解释,下面是一个子串的next数组的值,可以看到这个子串的对称程度很高,所以next值都比较大。

位置i

0

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

前缀next[i]

0

0

0

0

1

2

3

1

2

3

4

5

6

7

4

0

子串

a

g

c

t

a

g

c

a

g

c

t

a

g

c

t

g

申明一下:下面说的对称不是中心对称,而是中心字符块对称,比如不是abccba,而是abcabc这种对称。

(1)逐个查找对称串。

这个很简单,我们只要循环遍历这个子串,分别看前1个字符,前2个字符,3个... i个 最后到15个。

第1个a无对称,所以对称程度0

前两个ag无对称,所以也是0

依次类推前面0-4都一样是0

前5个agcta,可以看到这个串有一个a相等,所以对称程度为1前6个agctag,看得到ag和ag对成,对称程度为2

这里要注意了,想是这样想,编程怎么实现呢?

只要按照下面的规则:

a、当前面字符的前一个字符的对称程度为0的时候,只要将当前字符与子串第一个字符进行比较。这个很好理解啊,前面都是0,说明都不对称了,如果多加了一个字符,要对称的话最多是当前的和第一个对称。比如agcta这个里面t的是0,那么后面的a的对称程度只需要看它是不是等于第一个字符a了。

 

b、按照这个推理,我们就可以总结一个规律,不仅前面是0呀,如果前面一个字符的next值是1,那么我们就把当前字符与子串第二个字符进行比较,因为前面的是1,说明前面的字符已经和第一个相等了,如果这个又与第二个相等了,说明对称程度就是2了。有两个字符对称了。比如上面agctag,倒数第二个a的next是1,说明它和第一个a对称了,接着我们就把最后一个g与第二个g比较,又相等,自然对称成都就累加了,就是2了。

 

c、按照上面的推理,如果一直相等,就一直累加,可以一直推啊,推到这里应该一点难度都没有吧,如果你觉得有难度说明我写的太失败了。

当然不可能会那么顺利让我们一直对称下去,如果遇到下一个不相等了,那么说明不能继承前面的对称性了,这种情况只能说明没有那么多对称了,但是不能说明一点对称性都没有,所以遇到这种情况就要重新来考虑,这个也是难点所在。

 

(2)回头来找对称性

这里已经不能继承前面了,但是还是找对称成都嘛,最愚蠢的做法大不了写一个子函数,查找这个字符串的最大对称程度,怎么写方法很多吧,比如查找出所有的当前字符串,然后向前走,看是否一直相等,最后走到子串开头,当然这个是最蠢的,我们一般看到的KMP都是优化过的,因为这个串是有规律的。

在这里依然用上面表中一段来举个例子:   

位置i=0到14如下,我加的括号只是用来说明问题:

(a g c t a g c )( a g c t a g c) t

我们可以看到这段,最后这个t之前的对称程度分别是:1,2,3,4,5,6,7,倒数第二个c往前看有7个字符对称,所以对称为7。但是到最后这个t就没有继承前面的对称程度next值,所以这个t的对称性就要重新来求。

这里首要要申明几个事实

1、t 如果要存在对称性,那么对称程度肯定比前面这个c 的对称程度小,所以要找个更小的对称,这个不用解释了吧,如果大那么t就继承前面的对称性了。

2、要找更小的对称,必然在对称内部还存在子对称,而且这个t必须紧接着在子对称之后。

如下图说明。

 

 

 

从上面的理论我们就能得到下面的前缀next数组的求解算法。

void SetPrefix(const char *Pattern, int prefix[])

{

     int len=CharLen(Pattern);//模式字符串长度。

     prefix[0]=0;

     for(int i=1; i<len; i++)

     {

         int k=prefix[i-1];

         //不断递归判断是否存在子对称,k=0说明不再有子对称,Pattern[i] != Pattern[k]说明虽然对称,但是对称后面的值和当前的字符值不相等,所以继续递推

         while( Pattern[i] != Pattern[k]  &&  k!=0 )               

             k=prefix[k-1];     //继续递归

         if( Pattern[i] == Pattern[k])//找到了这个子对称,或者是直接继承了前面的对称性,这两种都在前面的基础上++

              prefix[i]=k+1;

         else

              prefix[i]=0;       //如果遍历了所有子对称都无效,说明这个新字符不具有对称性,清0

     }

}

通过这个说明,估计能够理解KMP的next求法原理了,剩下的就很简单了。我自己也有点晕了,实在不喜欢那些数学公式,所以用形象逻辑思维方法总结了一下。

////////

KMP还有一种写法:这个写法是经过N个人优化的:

技术分享图片
 1 int  j = -1,  i = 0;
 2 next[0] = -1;
 3 while(i < len)
 4 {
 5           if(j == -1 || ss[i] == ss[j])
 6          {
 7 
 8                     i++;
 9                     j++;
10                     next[i] = j;
11          }
12          else
13         {
14                    j = next[j];
15         }
16 }
技术分享图片

 

原文转自:http://blog.csdn.net/yearn520/article/details/6729426

kmp算法详细解释,带你理解k=next[k](逐代码分析)(代码片段)

文章目录前言一、BF解法二、KMP算法真前缀,真后缀KMP原理三、next数组next数组计算方法四、KMP算法实现总结前言给定一个主字符串T以及一个模式字符串P,判断P是不是T的子串,如果是则返回P在T中第一个元素的位置&#... 查看详情

kmp算法

JUly的文章:从头到尾彻底理解KMP KMP算法:复杂度:线性PMT数组:PMT中的值是字符串的前缀集合与后缀集合的交集中最长元素的长度。next数组:是将PMT数组向后偏移一位得到的数组。基于next数组进行匹配。 next数组的求... 查看详情

关于kmp算法中,获取next数组算法的理解

参考:KMP入门级别算法详解--终于解决了(next数组详解)https://blog.csdn.net/lee18254290736/article/details/77278769在这里讨论的next数组的含义为模式串p[j]之前前缀和后缀相等的个数,若都不相等则为0。(特殊情况,没有前缀和后缀时,则... 查看详情

kmp算法(代码片段)

目录KMP算法基本思想计算next数组前缀和后缀公共部分的最大长度next数组匹配字符串KMP算法基本思想算法由两部分组成计算ptr每一位及之前的字符串中,前缀和后缀公共部分的最大长度的next数组匹配ptr和str,当ptr失配时,利用nex... 查看详情

kmp算法浅谈(代码片段)

Kmp算法浅谈一.Kmp算法思想在主串和模式串进行匹配时,利用next数组不改变主串的匹配指针而是改变模式串的匹配指针,减少大量的重复匹配时间。在Kmp算法中,next数组的构建是整个Kmp算法的核心所在。二.Kmp核心之next数组的构... 查看详情

kmp算法求next数组

next数组的求解方法是:第一位的next值为0,第二位的next值为1。后面求解每一位的next值时,根据前一位进行比较。首先将前一位与其next值对应的内容进行比较,如果相等,则该位的next值就是前一位的next值加上1;如果不等,向... 查看详情

数据结构-串手算kmp算法的next和nextval数组(代码片段)

手算KMP算法的next数组例:求串’ababaaababaa’的next数组手算KMP算法的nextval数组nextval数组可由next数组求得,具体求法看以下代码://由next数组求得nextval数组strings;//模式串for(inti=0;i<s.length();i++)if(s[i]!=s[ne 查看详情

kmp算法——next数组求法

  j12345678910模式串abcabcacab部分匹配值0000123401next[]0111234512 "部分匹配值"是"前缀"和"后缀"的最长的共有元素的长度。以"ABCDABD"为例,      - "A"的前缀和后缀都为空集,共有元素的长度为0; ... 查看详情

kmp算法(代码片段)

...把子串的next[j]位置移动至与主串当前位置比较;因此KMP算法的主要过程就在于求子串的next数组了。 2.nextval数组:对next数组进一步改进,如果跳转到的nex 查看详情

kmp算法

问题:在字符串S中找到与字符串M相匹配的子串步骤:1:构造字符串M的next数组。目的:匹配时,当M[i]与S[j]不匹配了,确定能将M向后移动的最多位数。解释:next数组中保存的是在M[i]之前,与M起始串相匹配的最大子串。换个角... 查看详情

数据结构串---kmp模式匹配算法之获取next数组(代码片段)

(一)获取模式串T的next数组值1.回顾我们所知道的KMP算法next数组的作用next[j]表示当前模式串T的j下标对目标串S的i值失配时,我们应该使用模式串的下标为next[j]接着去和目标串失配的i值进行匹配而KMP算法的next求值函数我们可... 查看详情

kmp算法(next数组方法)(代码片段)

KMP算法之前需要说一点串的问题:串:字符串:ASCII码为基本数据形成的一堆线性结构。串是一个线性结构;它的存储形式:typedefstructSTRING  CHARACTER*head;  intlength;;朴素的串匹配算法:设文本串text="ababcabcacbab",模式串为patten=... 查看详情

顺序串的bf算法kmp(找next数组的值)算法

bf算法难度一般,kmp算法难度一般但是其中的找next[j]的算法比较难,很多小伙伴不好听明白,在这我推荐你们看这个链接的视频(https://www.bilibili.com/video/av714697013/)讲解的很明白通过动画理解代码的意义,这个不好理解多看多... 查看详情

kmp算法(代码片段)

概述KMP(Knuth-Morris-Pratt)算法是一种用来解决字符串匹配问题的算法,时间复杂度为O(n+m),主要思想是当模式串与主串发生失配时,不必从头开始匹配,而是滑动到已经匹配的部分next数组在KMP算法中,next数组用来存储一段子串最... 查看详情

kmp的next数组求法详解

近几天学习kmp算法,在next数组求解上受苦颇深,看了不少博客,感觉写得都不够清晰,所以想按照自己理解的过程来尝试写一下,也便于以后温习。关于kmp算法的介绍,网上博文有很多,就不再赘述&#x... 查看详情

kmp算法

   KMP的精髓就在于,用了一个线性的算法,得到了每次在pattern[j]发生失配时,应该让pattern往后移动多少步,这个值对应于pattern[0,j-1]的最长相等{前缀、后缀}的长度。这些值所存的数组叫做next数组。关键是在于了解ne... 查看详情

kmp算法

KMP算法(三个人名字开头字母)对BF算法进行了改进,省去了一部分没必要的比较,提高了算法的效率。K,M,P这三个人发现了BF算法中一些模式中遗憾的用于模式匹配的信息,这种信息就是模式匹配中的“部分匹配“的信息。首先... 查看详情

kmp算法理解与next数组的实现

昨天在看KMP算法,觉得很多资料写的不太容易理解自己整理了一份,欢迎讨论([email protected])因为是手机码的字直接转成了图片,有点长啊。。。650)this.width=650;"src="http://s4.51cto.com/wyfs02/M01/87/16/wKiom1fTizvTBLKlABDxAgMqq4o352.jpg-wh_5... 查看详情