一个全新的视角来看KMP算法(简单!形象!)

前面《字符串匹配》中我们介绍了KMP算法,《KMP算法Java实现》中给出了KMP算法的实现。

KMP算法很多人都说难,我第一次从《算法导论》中看到的时候也觉得难,后来重看算导时自己推导了一遍,觉得不难了,但是还是感觉印象不深,推导过后一段时间又会很模糊,下次遇到又得重新推,如此往复,浪费了大量时间。KMP算法原理上并不难,但是之所以给人难的感觉是因为它不够直观。今天突发奇想想到了一种将KMP算法图形化的方法,我们对图像的认识远比字符串深刻,下面就予以介绍。

我们还是取之前文章中的例子,T和P分别如下:

b a c b a b a b a a b c b a   T

            a b a b a c a  P

如何将其图形化呢?

因为要将其变为二维图像,我们需要两个量作为每个元素的坐标,第一个量就取元素在字符串中的位置(从0开始),那么第二个量呢?也很简单,取字母在字符表中的位置(从0开始),这样就得到一个坐标,然后我们将其标记到坐标轴中,如下分别是T和P的图形:

一个全新的视角来看KMP算法(简单!形象!)_第1张图片

一个全新的视角来看KMP算法(简单!形象!)_第2张图片

图形已经在上面了,那么字符串匹配问题现在就可以重新描述为:给定一个图形T和一个图形P,如果P在T中以偏移s出现(吻合),那么称s是有效偏移;否则,称它是无效偏移。现在的问题就是找到所有的有效偏移。

有两条很明显(但是很重要)的性质:

性质1. 如果P出现在T中,那么其形状是吻合的,而且对应的每一个部分也是吻合的

性质2. P中可能会有多个形状相同的区域,比如下图中的0-2和2-4,pi[i]正是记录这些信息的,比如这里的pi[4] = 3,也就是图中的坐标2.


现在我们根据伪代码来重新看一下匹配过程

KMP-MATCHER(T,P)

1. n = T.length

2. m = P.length

3. pi = COMPUTE-PREFIX-FUNCTION(P)

4. q = 0         //number of characters matched

5. for i = 1 to n

6. while q>0 and P[q+1] != T[i]

7.       q = pi[q]

8. if P[q+1] == T[i]

9.       q = q+1

10. if q == m

11.      print "Pattern occurs with shift" i-m

12.      q = pi[q]    //look for next match

从第5行开始,第5行开始的for循环依次比对T中的每个位置,如果不匹配,按照我们上面给出的两条性质,要想找到与当前位置i匹配的T,那么它的前面部分也必须匹配(性质1),那么我们就应该找到前面部分匹配的位置来进行比对,根据性质2,T中可能不止有一个部分形状相同,因此我们应该遍历前面所有可能使得当前匹配的part,直到找到一个或者遍历完(一个都没发现)。而这正是6-7行的while循环所做的工作。如果当前吻合了,继续比对下一部分(8-9行),当整个P都吻合了(第10行),输出位置(第11行),继续下一次的比对(第12行)。

就写到这里,望仔细体会!








你可能感兴趣的:(算法与数据结构)