KMP串匹配算法

KMP实现思路:
1.代码;2.穷举;3.i不回退; 4. j回退到一定位置; 5. p串规律; 6. next[j]=k;

1.代码

在这里插入图片描述

2. 穷举

穷举法就时把所有的情况的列举出来,不用在乎规律什么的。
例1:
s: a b a b a c
p: a b a c
解:s串的字串从前到后依此是(a b a b)、(b a b a )、(a b a c),明显可以看出s串第三个子集与p串相匹配,代码很简单:
在这里插入图片描述
上面的代码逻辑也很简单,分别给s串和p串一个计数下标 i 和 j ,依此比较,如果发现不匹配的情况,j回到0位置,i回到起始位置的下一个位置,直到找到串匹配,或 者i 到尾部。

i 和 j 的回退给这个程序造成了十分大的消耗,那么可不可以不让 i 和 j 回退?*

3. i 的回退

我们来举一个特殊的例子,例2:
s:a b c a b c d
p:a b c d
一眼可以看出s串从s[3:6]与p串相匹配。从计算机的角度看,从s[0]开始,直到s[3]失配,按照之前的算法,要回到s[1],它是消耗的主要原因之一,而我们现在的做法是不回退,直接从s[3]开始与p[0]比较,如此就大大降低了时间复杂度。

这里肯定就有人要问:这里只是这个例子计较特殊,p串abcd各不相同,造成了你的i不用回退,如果你拿例1来看,按照这种做法做,结果找不到串匹配,那不就错了吗?

4. p串首位重复

我们再看例1:
s: a b a b a c
p: a b a c
按照i不回退的思路去做,在s[3]处失配,再从s[3]开始,直到结束都找不到串匹配,但世界结果是存在的,那么该怎么办?
首先,我们找出例1和例2的区别,分辨出那些是用i不回退的思想做不了的,答案也是显而易见的,例1的p串有重复的元素a,那么是不是有重复的元素就不行?我们来看例3:
s: a b c a b c b
p: a b c b
这里也有p串也有重复的元素b,但是它根本不用回退。原因是什么?答案是判断p串首位是否有重复的。
在例1中,p串首位元素a存在从重复,在这种情况失配下,我们要在s串中寻找下一个a的为位置并回退到此处,如果没有就不用会退了。例子中a的下一个位置是s[2],所以s要从s[3]回退到s[2]。

但是,是不是i 一定要回退呢,有没有更简单的方法?

5. j 的回退

要解决这个问题我们继续对例4和例5:
s: a b c a d d d e…
p: a b c a d d d d
例4在s串在s[7]处失配,然后s回退到s[3],再继续比较,s[4]与p[1]再次失配,而且s[4]—s[7]之间没有a,所以s再次前进到s[7],然后继续匹配。那么, i 能不能不回退?直接比较s[7]==p[0]?。
s: a b c a b c …
p: a b c a b d
例5在s串在s[5]处失配,然后回退到s[3],s[3]=p[0],s[4]=p[1],直到比较s[5]==p[2],同问, i 能不能不回退?能不能直接比较s[5]==p[2]?

例4和例5给出了,i 回退到下一个a处的两种情况,即匹配和失配。实际上产生这两种情况的原因不再s身上,而在p身上。
我们仔细观察例5,s[3]与s[4]与p串的元素比较了两次,第一次,s[5]失配前,s[3]=p[3],s[4]=p[4]第二次,s[5]失配后,s[3]=p[0],s[4]=p[1],这里可以推出p[0]=p[3], p[1]=p[4],实际上,这是什么?这是p串自身的特性,跟有没有s[3]s[4]一点关系都没有:p串自身有这种"重复性规律",i 不回退,且 j 也不会回退到0,就和例5一样,直接比较s[5]==p[2];p串自身没有这种"重复性规律",i 不回退, j 回退到0,他就和例4一样,直接比较s[7]==p[0];

至此,我们实现了,i不会退,j根据"重复性规律"回退。那么s串这种"重复性规律"怎么得出?

6. p串的"重复性规律"

我们来观察例5的p串:a b c a b d
不要去考虑s串,认为它存在,或者s串是任意一个字符串,p串要与一个任意的字符串匹配。
所以,p串可能在任意一个位置失配,
如果在p[2]处失配,由于p[0]!=p[1],所以j 回退到p[0]。
如果在p[3]处失配,由于p[0]!=p[2],所以j 回退到p[0]。
如果在p[4]处失配,由于p[0]=p[3],所以j 回退到p[1]。
如果在p[5]处失配,由于p[0]=p[3],p[1]=p[4],所以j 回退到p[2]。
总结规律,我们得出:当在p[j]处失配时,如果p[0 ~ k-1] = p[ j-k ~ k-1],那么j回退到k的位置。

所以我们,要做得就是求出p串每一个位置上,如果失配,需要回退的位置k。

7. 求next[i] = k ;

直接来看代码:
在这里插入图片描述
函数思想是由next[j] =k,得出next[j+1]的值
所以我们先对前两个做出规定:next[0] = -1, next[1]= 0
然后自然是比较p[j]==p[k]?
如果等于,自然很好理解next[j+1] =k+1
如果不等于,k=next[j],这个比较复杂,以后再补。

猜你喜欢

转载自blog.csdn.net/qq_42953408/article/details/88528615