KMP算法的一次理解(三)

前面说到了字符串的模式匹配的暴力方法,同时在暴力方法的基础上做了一些改进:不让主串的匹配指针i回溯,通过发掘模式串的一些特性,不断的修改模式串的匹配指针。但是模式串的匹配指针怎么修改呢,那就得要结合其自身的一些特性,然后产生相应的修改值,记录在next[j]这个数组中。



1. 寻找前缀后缀最长公共元素长度:

对于,寻找模式串P中长度最大且相等的前缀和后缀。如果存在 = ,那么在包含pj的模式串中有最大长度为k+1的相同前缀后缀。举个例子,如果给定的模式串为“abab”,那么它的各个子串的前缀后缀的公共元素的最大长度如下表格所示:

比如对于字符串aba来说,它有长度为1的相同前缀后缀a;而对于字符串abab来说,它有长度为2的相同前缀后缀ab(相同前缀后缀的长度为k + 1,k + 1 = 2)。

2. 求next数组:

next 数组考虑的是除当前字符外的最长相同前缀后缀,为什么是除了当前字符呢?回顾前面的两个例子中的模式串“abcdex”和“abcabx”,发现当匹配指针到达某一个字符并且要用到next数组的时候,这个字符一定是匹配失败的字符,在源代码中也可看到这一点,所以这个字符无论如果都要再比较的,不能从算法上逃过去,。所以通过第1步骤求得各个前缀后缀的公共元素的最大长度后,只要稍作变形即可:将第1步骤中求得的值整体减去1(0值保持不变,因为最长相同前缀后缀不可能为负数),然后初值赋为-1(这里的-1不代表最长相同前缀后缀的长度,仅仅代表这个字符是模式串的首字符),如下表格所示:

比如对于aba来说,第3个字符a之前的字符串ab中有长度为0的相同前缀后缀,所以第3个字符a对应的next值为0;而对于abab来说,第4个字符b之前的字符串aba中有长度为1的相同前缀后缀a,所以第4个字符b对应的next值为1(相同前缀后缀的长度为k,k = 1)。

3. 根据next数组进行匹配:

说了上面的两个步骤,下面进入正题,怎么根据next数组的值,在保证i值不回溯的情况下,调整j的值进行匹配。

匹配失配,j = next [j],模式串相对于主串向右移动的位数为:j - next[j]。换言之,当模式串的后缀 跟文本串 匹配成功,但Pj si匹配失败时,因为next[j] = k,相当于在不包含pj的模式串中有最大长度为k 的相同前缀后缀,即,故令j = next[j],从而让模式串右移j - next[j] 位,使得模式串的前缀

对应着文本串 ,而后让Pksi 继续匹配。如下图所示:

综上,KMP的next 数组相当于告诉我们:当模式串中的某个字符跟文本串中的某个字符匹配失配时,模式串下一步应该跳到哪个位置。如模式串中在j 处的字符跟文本串在i 处的字符匹配失配时,下一步用next [j] 处的字符继续跟文本串i 处的字符匹配,相当于模式串向右移动 j - next[j] 位。



接下来看一个例子,具体的解释上面的东西:

1. 寻找最长前缀后缀:

如果给定的模式串是:“ABCDABD”,从左至右遍历整个模式串,其各个子串的前缀后缀分别如下表格所示:

也就是说,原模式串子串对应的各个前缀后缀的公共元素的最大长度表为(下简称《最大长度表》):

2. 基于《最大长度表匹配》:

因为模式串中首尾可能会有重复的字符,故可得出下述结论:

失配时,模式串向右移动的位数为:已匹配字符数 - 失配字符的上一位字符所对应的最大长度值

下面,结合之前的《最大长度表》和上述结论,进行字符串的匹配。如果给定文本串“BBC ABCDAB ABCDABCDABDE”,和模式串“ABCDABD”,现在要拿模式串去跟文本串匹配,这里先直接用最大前缀后缀公共元素长度表,先不用next数据。还有一点就是失配字符的前一个字符对应的最大长度值其实就是下一次匹配时模式串的匹配指针的值,例子中用的是相对于主串的右偏移量,当然两种理解方式都是可以的。如下图所示:

1. 因为模式串中的字符A跟文本串中的字符B、B、C、空格一开始就不匹配,所以不必考虑结论,直接将模式串不断的右移一位即可,直到模式串中的字符A跟文本串的第5个字符A匹配成功:

2. 继续往后匹配,当模式串最后一个字符D跟文本串匹配时失配,显而易见,模式串需要向右移动。但向右移动多少位呢?因为此时已经匹配的字符数为6个(ABCDAB),然后根据《最大长度表》可得失配字符D的上一位字符B对应的长度值为2,所以根据之前的结论,可知需要向右移动6 - 2 = 4 位。

3. 模式串向右移动4位后,发现C处再度失配,因为此时已经匹配了2个字符(AB),且上一位字符B对应的最大长度值为0,所以向右移动:2 - 0 =2 位。

4. A与空格失配,向右移动1 位。

5. 继续比较,发现D与C 失配,故向右移动的位数为:已匹配的字符数6减去上一位字符B对应的最大长度2,即向右移动6 - 2 = 4 位。

6. 经历第5步后,发现匹配成功,过程结束。

这样整个匹配过程就结束了,即使后面再有可以匹配成功的字符串,也不会匹配了。当然如果再次调用匹配函数也是可以的。

通过上述匹配过程可以看出,问题的关键就是寻找模式串中最大长度的相同前缀和后缀,找到了模式串中每个字符之前的前缀和后缀公共部分的最大长度后,便可基于此匹配。而这个最大长度便正是next 数组要表达的含义。

时间: 2024-08-11 07:42:37

KMP算法的一次理解(三)的相关文章

KMP算法详解 --- 彻头彻尾理解KMP算法

[经典算法]——KMP,深入讲解next数组的求解 前言 之前对kmp算法虽然了解它的原理,即求出P0···Pi的最大相同前后缀长度k:但是问题在于如何求出这个最大前后缀长度呢?我觉得网上很多帖子都说的不是很清楚,总感觉没有把那层纸戳破,后来翻看算法导论,32章 字符串匹配虽然讲到了对前后缀计算的正确性,但是大量的推理证明不大好理解,没有与程序结合起来讲.今天我在这里讲一讲我的一些理解,希望大家多多指教,如果有不清楚的或错误的请给我留言. 1.kmp算法的原理: 本部分内容转自:http://w

KMP算法的一次理解

1. 引言 在一个大的字符串中对一个小的子串进行定位称为字符串的模式匹配,这应该算是字符串中最重要的一个操作之一了.KMP本身不复杂,但网上绝大部分的文章把它讲混乱了.下面,咱们从暴力匹配算法讲起,随后阐述KMP的流程步骤.next 数组的简单求解.递推原理.代码求解,接着基于next 数组匹配,谈到有限状态自动机,next 数组的优化,KMP的时间复杂度分析,最后简要介绍两个KMP的扩展算法. 2. 暴力匹配算法 2.1 问题描述: 有一个文本串s和一个模式串p,现在要查找p在s中的位置,怎么

KMP算法的一次理解(中)

在开始KMP算法之前,先来回顾一下字符串模式匹配的暴力法,具体的过程如下图所示: 注:图中两个串的匹配都是从1开始的,代码中的匹配都是从0开始的. 可以看到匹配主串的i的值是不断的回溯的,然而KMP三位大师发现这种回溯其实是不需要的,所以提出来这个算法来解决这个问题. 既然i值不能回溯了,也就是不能变小了,那么要考虑变化的就是j值了.为了能说清楚KMP算法,先以暴力算法为基础分析两个例子. 例1: 主串s="abcdefgab"模式串p="abcdex",也就是上面

学习记录---KMP算法-部分匹配表理解

如需转载,请保留本文链接. 看了 阮一峰 大神的字符串匹配的KMP算法后,关于部分匹配的部分并不是很理解,特意去看了阮大神文章中的英文链接,这里写下自己的理解,用作学习记录. 阮大神文章链接:https://kb.cnblogs.com/page/176818/ Jake Boxer 英文博文链接:http://jakeboxer.com/blog/2009/12/13/the-knuth-morris-pratt-algorithm-in-my-own-words/ 关于前缀和后缀的定义,这里

KMP算法的一次理解(六)

在前面对KMP算法做了各种的讲解之后,现在要对这个算法做一个代码的实现了: int KmpSearch(char *s, char *p) { int i = 0; int j = 0; int sLen = strlen(s); int pLen = strlen(p); while(i < sLen && j < pLen) { //如果j = -1,或者当前字符匹配成功(即S[i] == P[j]),都令i++,j++ if(j == -1 || s[i] == p[j]

KMP算法的一次理解(上)

在一个大的字符串中对一个小的子串进行定位成为字符串的模式匹配,这应该算是字符串中最重要的一个操作之一了. 问题描述: 有一个文本串s和一个模式串p,现在要查找p在s中的位置,怎么查找? 如果用暴力匹配的思路,并假设文本串匹配到i位置,模式串匹配到j位置. 算法描述: 有关字符串的模式匹配,首先来看最简单的一个算法,那就是暴力法.具体的算法描述: (1)初始化i指向主串的初始位置,这里假设是主串的0位置:j指向子串的0位置. (2)若当前字符匹配成功,也就是s[i] == p[j],则i++,j+

字符串匹配与KMP算法笔记

>>字符串匹配问题 字符串匹配问题即在匹配串中寻找模式串是否出现, 首先想到的是使用暴力破解,也就是Brute Force(BF或蛮力搜索) 算法,将匹配串和模式串左对齐,然后从左向右一个一个进行比较, 如果不成功则模式串向右移动一个单位,直到匹配成功或者到达匹配串最后仍然不成功,返回失败. 很明显,这种算法有很多的地方可以优化,假设要搜索的串为S,长度为n,要匹配的串为M,长度为m,时间复杂度为O(nm). >>KMP算法 Knuth-Morris-Pratt算法以三个发明者命名

KMP算法具体解释(转)

作者:July. 出处:http://blog.csdn.net/v_JULY_v/. 引记 此前一天,一位MS的朋友邀我一起去与他讨论高速排序,红黑树,字典树,B树.后缀树,包含KMP算法,只有在解说KMP算法的时候,言语磕磕碰碰,我想,原因有二:1.博客内的东西不常回想,忘了不少:2.便是我对KMP算法的理解还不够彻底,自不用说解说自如,运用自如了.所以,特再写本篇文章.因为此前,个人已经写过关于KMP算法的两篇文章,所以,本文名为:KMP算法之总结篇. 本文分为例如以下六个部分: 第一部分

“浅析kmp算法”

"浅析kmp算法" By 钟桓 9月 16 2014 更新日期:9月 16 2014 文章目录 1. 暴力匹配: 2. 真前缀和真后缀,部分匹配值 3. 如何使用部分匹配值呢? 4. 寻找部分匹配值 5. 拓展 5.1. 最小覆盖字串 6. 参考资料 首先,KMP是一个字符串匹配算法,什么是字符串匹配呢?简单地说,有一个字符串"BBC ABCDAB ABCDABCDABDE",我想知道这个字符串里面是否有"ABCDABD":我想,你的脑海中马上就