您的位置:首页 > 理论基础 > 数据结构算法

理解和实现KMP算法

2017-02-01 18:24 281 查看
模式匹配是数据结构中字符串的一种基本运算,给定一个子串,要求在某个字符串中找出与该子串相同的所有子串,这就是模式匹配。KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt同时发现,因此人们称它为克努特—莫里斯—普拉特操作(简称KMP算法)。

简单模式匹配

@@又称朴素模式匹配,从目标串的的第一个字符起与模式串的第一个字符比较,若相等,则继续对字符进行后续的比较,否则目标串从第二个字符起与模式串的第一个字符重新比较,直至模式串中的每个字符依次和目标串中的一个连续的字符序列相等为止,此时称为匹配成功,否则匹配失败。

KMP算法

@@当前一次比较有部分匹配时,我们获得了待查找文本的一部分信息,即匹配重合的部分。朴素模式匹配事实上放弃了这部分信息而重新匹配,从信息的利用率来讲这是相当浪费资源的。如何将获得的信息充分地利用呢?于是就产生了KMP算法。

@@KMP算法说,每次匹配失败之后,不用一个一个地向右重新匹配,而是直接跳过n次匹配,进行第n+1次匹配。至于n的确定,只于模式串有关。

(defun make-kmptable (pattern)
(let* ((len (length pattern))
(kmptable (make-array (- len 1) :initial-element 0))
(k 0))
(do ((i 1 (+ i 1)))
((>= i (- len 1))
kmptable);最后返回的是这个数组
(do ()
((not (and (< i (- len 1)) (char= (aref pattern i) (aref pattern k)))))
;新加入在已匹配后缀的后一个字符与已匹配前缀的后一个字符相同
;那么说明此时前缀和后缀的匹配长度增加一
;继续在已匹配后缀后面加一个字符,重复直到所有字符都匹配过
(progn (setf (aref kmptable i) (+ k 1))
(setf k (+ k 1))
(setf i (+ i 1))))
;新加入在已匹配后缀的后一个字符与已匹配前缀的后一个字符不相同
;此时回溯匹配,在已匹配前缀中找匹配的前后缀作为新的前后缀,重复do循环
;如果k为0说明已回溯到第一个字符仍无匹配,则匹配长度为0,由于初始为0所以不用修改
(if (> k 0)
(progn (setf k (aref kmptable k))
;由于do循环中当k=0时i要加一,而实际此处i需要保持不变,所以减一
(setf i (- i 1)))))))
;;在text中搜索pattern,如找到则返回pattern在text首次出现的索引,否则返回notfound
(defun kmp-search (text pattern)
(let ((kmptable (make-kmptable pattern));获得kmptable
(lent (length text))
(lenp (length pattern));获得text和pattern的长度
(i 0)
(j 0))
(do ()
((>= i lent) 'notfound);匹配结束,未找到匹配
(do ()
((char/= (aref text (+ i j)) (aref pattern j))
;匹配失败,如果第一个字符就匹配失败,直接i+1重新匹配
(if (= j 0)
(setf i (+ i 1))
;j>0时至少有一个字符匹配,查表得到匹配前后缀长度
;注意j是不匹配的字符索引,仅匹配到j-1,查表时应用j-1作为索引
;i跳跃的长度为(j-前后缀长度)
(progn (setf i (+ i (- j (svref kmptable (- j 1)))))
(setf j 0))))
;一个字符匹配成功进行下一个字符的匹配
(setf j (+ j 1))
;如果j达到pattern的长度说明完全匹配成功,返回索引
(if (= j lenp)
(return-from kmp-search i))))))


kmp算法大概就是这样。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  数据结构 算法 kmp