赞
踩
KMP的经典思想就是:当出现字符串不匹配时,可以记录一部分之前已经匹配的文本的内容,利用这些信息避免从头再去做匹配。(代码随想录)
KMP是由三位学者发明的,Knuth,Morrist,Pratt,取了三位学者名字的首字母。
KMP主要应用在字符串匹配上。主要思想是当出现字符串不匹配时,可以知道一部分之前已经匹配的文本内容,可以利用这些信息避免从头再去做匹配了。重点是如何激励已经匹配的文本内容。
前缀表是用来回退的,它记录了模式串与文本串不匹配的时候,模式串应该从哪里重新匹配。
前缀表是:记录下标i之前(包括i)的字符串中,有多大长度相同前缀后缀
例:要在文本串aabaabaafa中查找是否出现过一个模式串aabaaf
如下图可以看出,文本串中的第六个字符b和模式串中第六个字符f不匹配了。
若使用前缀表,就看可以不用从头匹配。而是从上次已经匹配的内容开始匹配,找到模式串中第三个字符b继续开始匹配。
那么前缀表是如何记录的呢?
前缀表的任务是当前位置匹配失败,找到之前已经匹配的位置,在重新匹配,也就意味着在某个字符匹配失败,前缀表会告诉你下一步匹配,模式串应该跳到哪里。
前缀是指不包含最后一个字符的所有以第一个字符开头的连续子串。
后缀是指不包含第一个字符的所有以最后一个字符开头的连续子串。
回顾一下,刚刚在匹配过程中,在下标5的地方不匹配,如图
然后就找到了下标2,指向b,继续匹配,如图
解释:下标5之前这部分的字符串(aabaa)的最长相等的前缀和后缀是子字符串aa,因为找到了最长相等的前缀和后缀,匹配失败的位置就是后缀字串的后面,那么我们就只需找到与其相同的前缀后面重新匹配就可以了。
长度为前1个字符的子串a,最长相同前后缀的长度为0。
长度为前2个字符的子串aa,最长相同前后缀的长度为1。
长度为前3个字符的子串aab,最长相同前后缀的长度为0。
长度为前4个字符的子串aaba,最长相同前后缀的长度为1。
长度为前5个字符的子串aabaa,最长相同前后缀的长度为2。
长度为前6个字符的子串aabaaf,最长相同前后缀的长度为0。
可以看出,模式串与前缀表对于位置的数字表示的就是:下标i之前(包括i)的字符串中,有多大长度的相同前缀后缀。
找到的不匹配的位置,此时看它的前一个字符的前缀表数值是多少(因为要找前面字符串的最长相同的前缀和后缀),然后移动到下标为那个数值的位置
next数组就是前缀表,很多实现是把前缀表统一减一,但也可以就是前缀表。
文本串长度为n,模式串长度为m。因为在匹配过程中,根据前缀表不断调整匹配的位置,可以看出匹配的过程是O(n),之前还要单独生成next数组,时间复杂度是O(m),所以整个KMP算法的时间复杂度是O(m+n).暴力法的时间复杂度是O(m*n)。
- //函数参数为指向next数组的指针,和一个字符串
- void getNext(int* next, const string& s){
- int j = -1;//j指向前缀末尾的位置
- next[0] = j;
- for(int i = 1; i < s.size(); i++) { // 注意i从1开始
- while (j >= 0 && s[i] != s[j + 1]) { // 前后缀不相同了
- j = next[j]; // 向前回退,next[j]就是记录着j(包括j)之前的子串的相同前后缀的长度。那么 s[i] 与 s[j+1] 不相同,就要找 j+1前一个元素在next数组里的值(就是next[j])。
-
- }
- if (s[i] == s[j + 1]) { // 如果 s[i] 与 s[j + 1] 相同,那么就同时向后移动i 和j 说明找到了相同的前后缀,同时还要将j(前缀的长度)赋给next[i], 因为next[i]要记录相同前后缀的长度。
- j++;
- }
- next[i] = j; // 将j(前缀的长度)赋给next[i]
- }
- }
-
- int j = -1; // 定义两个下标j 指向模式串起始位置,因为next数组里记录的起始位置为-1。
- for (int i = 0; i < s.size(); i++) { //i指向文本串起始位置,注意i就从0开始遍历文本串
- while(j >= 0 && s[i] != t[j + 1]) { // 不匹配
- j = next[j]; // j 寻找之前匹配的位置,就要从next数组里寻找下一个匹配的位置
- }
- if (s[i] == t[j + 1]) { // 匹配,j和i同时向后移动
- j++; // i的增加在for循环里
- }
- if (j == (t.size() - 1) ) { // 文本串s里出现了模式串t
- return (i - t.size() + 1);
- }
- }
-
- class Solution {
- public:
- void getNext(int* next, const string& s)
- {
- int j = -1;
- next[0] = j;
- for(int i=1;i<s.size();i++)
- {
- while(j>=0 && s[i]!=s[j+1])
- {
- j=next[j];
- }
- if(s[i] == s[j+1])
- {
- j++;
- }
- next[i]=j;
- }
- }
-
- int strStr(string haystack, string needle) {
- if(needle.size() == 0) return 0;
- int next[needle.size()];
- getNext(next,needle);
- int j=-1;
- for(int i=0;i<haystack.size();i++)
- {
- while(j>=0 && haystack[i] != needle[j+1])
- {
- j=next[j];
- }
- if(haystack[i] == needle[j+1])
- {
- j++;
- }
- if(j == (needle.size()-1))
- {
- return (i-needle.size()+1);
- }
- }
- return -1;
- }
- };
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。