本文内容主要来自天勤公开课的KMP算法讲解
快速从一个主串中找到一段和模式串(子串)相同的串
模式串与主串左端对齐,开始逐个比较: 当扫描到某一个位置发现不匹配后,就让模式串后移一位,并且让比较指针回到模式串的第一位,再开始下一轮的比较: 这个过程中,比较指针从后面回到前面,称为比较指针的回溯。指针回溯是造成这个简单匹配算法效率比较低的主要原因。
但是KMP算法能做到仅让模式串后移,指针不回溯,提高算法的效率。
回到上一步观察,在不匹配的位置之前的部分,主串与模式串是完全匹配的
并且模式串的左右两端有两个子串(AB子串)是完全匹配的,称为模式串的公共前后缀。 下面这一步是KMP算法的关键: 直接往前移动模式串(指针没有移动),使得模式串的公共前后缀里的前缀直接移动到了原来的后缀所在的位置 这样可以保证,当前比较指针所在的位置左边的部分上下是匹配的。 (因为公共前后缀是匹配的,移动模式串之前,指针左边的部分上下是匹配的,把前缀移到后缀的位置,上下也是匹配的。) 如果匹配的模式串中有多个公共前后缀,要取最长的公共前后缀 此时指针继续往后扫描,直到又发生不匹配 需要寻找最长的公共前后缀,此时的最长公共前后缀为A:
下一步就是移动模式串,使得模式串的前缀与原来后缀的位置重合 这时发现模式串的长度已经超出主串的范围了,就可以判定匹配失败。 显然,这种方法的比较次数比简单匹配算法少得多!
从头开始匹配,指针找到不匹配的位置 此时需要寻找最长前后缀: 发现这个还不是最长的前后缀,再找 如果找到的公共前后缀与比较指针左边的子串长度相同了,就无法移动模式串了,这样是没有意义的,所以找公共前后缀需要加一个限制,就是不能超过比较指针左边的子串长度。
所以这个过程中最长的公共前后缀就是ABA
然后移动模式串,使前缀来到后缀的位置 然后找公共前后缀、移动模式串。指针后移逐个匹配,最后匹配成功
在计算机中,字符串通常都是存在一个字符数组中,数组在内存中是不能移动的,所以上述过程只是为了形象化的演示,现在需要把它转换为计算机能够处理的方式。 回到刚刚移动模式串的时候 在没有主串的情况下,一样可以移动到正确的位置,所以在KMP算法中,我们只用研究模式串,挖掘出模式串的相关信息,就可以让这个模式串和任何的主串进行匹配了 把模式串放到数组中,注意这个模式串在数组中是从下标1开始存的(也可以从0开始)
模式串1号位的情况如果在模式串第一个位置就发生不匹配,需要将模式串后移一位 转换成在计算机中就是将模式串1号位与主串下一位进行比较
模式串2号位的情况如果在模式串第二个位置发生不匹配,在前面找公共前后缀,找不到,则将模式串移动到当前位置 在计算机中表示为将模式串1号位与主串当前位置进行比较
模式串3号位的情况 3号位也没有找到公共前后缀,处理方法与2号一样 此时模式串的前三位发生不匹配时处理方式如图: 模式串4号位的情况看四号位,此时有公共前后缀了 理论上,需要将前缀的位置移到后缀的位置,模式串移动后指针应该指到数组的2号位 但在计算机中数组不能移动,要表示在计算机中就是模式串的2号位与当前位置比较
模式串5号位的情况 5号位的公共前后缀为AB 理论上,移动模式串后指针应该指到数组的3号位 但在计算机中数组不能移动,要表示在计算机中就是模式串的3号位与当前位置比较 模式串6号位的情况 找到公共前后缀为ABA,公共前后缀长度为3 然后理论上移动模式串,确定指针的位置,就是4号位 在计算机中就是用4号位与主串当前位置开始比较 此时出现一个规律:当前公共前后缀的长度为3,我们需要4号位与主串当前位置进行比较,结合前面几个的情况可以发现: 如果公共前后缀的长度位n,我们就需要用n+1号位与主串当前位置开始比较用这个规律我们就可以得到所有位置上的描述 可以发现,除了第一句话之外,其他的除了“几号位”以外都是一样的。 把第一句话标记为0 在计算机中就表示为: if(0) {按第一句话这种方式处理}
所以我们可以把这些数字(x号位)作为每一位的条件放在另一个数组中。根据这个数组的指示,我们就可以知道,在模式串中,任意一个位置发生不匹配,我们应该执行什么动作 这个数组就叫做next数组