【算法】kmp 的 getnext 函数解释

本人的理解,供参考,如有错误请指出谢谢

设子串:ababafgda

getnext 函数的目的就是找出子串的子串的共同前后缀的长度

设变量
j = 0:
k = 1:表示循环进度,也表示当前在那个子串的子串
len = strlen(子串) = 9

while(k < len - 1)

在这里插入图片描述
匹配失败
k++ ,进入下一个子串,在 +1 之前 k 表示的是 ab 子串
j = 0,肯定要归零,要不然怎么找前缀

在这里插入图片描述
匹配成功
此时 k = 2,就表示aba子串,那么aba子串的公共最长前后缀就是 1,这个 1 只能从 j 变量获得,此时 j = 0,所以
next[k] = j+1
就是说 aba子串的公共最长前后缀就是 1
匹配结束进入下一个子串,所以 k++,所以不论成功与否 k 都是要+1的
那么 j 呢,也是要 +1 的
为什么?

aba
要获得 aba 的前后缀需要比对 ab 中的a 和 ba 中的b 的匹配 和 ab中的b 和 ba 中的a 的匹配

这里需要分两种情况

如果 ab 是匹配成功的,那么ba 就是 ab 各自下标的+1的偏移
如果不匹配就需要从头开始匹配
显然 ab 是不匹配的,所以就需要匹配 ab 的a 和ba 的 a,如果成功就说明 aba 的公共最长前后缀就是 1,不成功就是0咯

例如:abab
aba 中 ab 的a 和ba 的 a 是匹配的(见上面),所以接下来就该匹配 两个b 了,这两个b 的下标就是aba 中两个a 的下标+1

之后重复下标+1和归零的操作即可

在这里插入图片描述
匹配成功
abab 的公共最长前后缀就是 aba 公共最长前后缀 +1

有没有发现 aba 的公共最长前后缀是前缀也就是 j+1
aba 是 j 指向 a,值就是 0 ,所以需要 +1之后才能赋值给 next
但在 abab中 j 指向 aba 中的 b,值就是1,也需要 +1之后才能赋值给 next
所以可以先 +1 在赋值给next

在这里插入图片描述
匹配成功
ababa 的公共最长前后缀就是 abab 公共最长前后缀 +1

在这里插入图片描述
匹配失败
ababaf 的公共最长前后缀为 0
j 有需要归零

在这里插入图片描述
匹配失败
ababafg 的公共最长前后缀为 0
j 归零
在这里插入图片描述
匹配失败
ababafgd 的公共最长前后缀为 0
j 归零

在这里插入图片描述
匹配成功
ababafgda 的公共最长前后缀为 j+1=0

但是并不需要公共最长前后缀了,已经匹配结束了

公共最长前后缀 是为失败后下一次跳过公共的内容做准备的

前提是失败后

同理解释为什么要设
j = 0:
k = 1

一个字符的公共最长前后缀就是 1,但是一个字符失败后肯定只跳过一个字符呀,所以公共最长前后缀就无意义了

在这里插入图片描述

根据所讲内容应该能写出以下代码

j = 0
k = 1
// k 表示循环进度
while(k < len-1) {
	// 这就是图中的两个字符的比对
	if(str[j] == str[k]) {
		++i;
		// k 表示循环进度,也表示当前在那个子串的子串
		next[k] = j;
	}else{
		j = 0 // j 归零
	}

	++j; // 无论如何都会 +1 
}

但是这个并不好,不好体验在next数组的值
目前next 的值为

str:    a  b  a  b  a  f  g  d  a 
next:   0  0  1  2  3  0  0  0  0

设在 ababcd 中搜索 ababa

ababcd 
ababa

ababcd 
  ababa

a 和 c 不匹配,所以要跳过什么?
跳过 ababa 的最长前后缀?看就能看出问题来,明显不是,而是跳过 ababa 中的 abab 的最长前后缀

那么如何获得 abab 的最长前后缀,是next[j-1]

设 j 是当前的位置

必须修改代码才能在使用的时候不用 -1

void mGetNext(char* str,int next[]) {
    int i = 0;
    int j = 1;
    int len = strlen(str);
    next[0] = -1;
    next[1] = 0; // 因为 j+1
    while(j < len-1) {

        if(str[i] == str[j]) {
            ++i;
            next[j+1] = i;
        }else{
            i = 0;
        }

        ++j;
    }
}

现在这代码和下面的代码是一样的效果

你应该看到过以下代码,

void GetNext(char* p,int next[])  
{  
    int pLen = strlen(p);  
    next[0] = -1;   // 利用 -1
    int k = -1;  
    int j = 0;  
    while (j < pLen - 1)  
    {  
        //p[k]表示前缀,p[j]表示后缀  
        if (k == -1 || p[j] == p[k])   
        {  
            ++k;  
            ++j;  
            next[j] = k;  
        }  
        else   
        {  
            k = next[k];  
        }  
    }  
}

该代码我是真的写不出来,
是我太菜了。?


关于 next[0] = -1 的想法,在我遇到的代码中充分利用了 -1 这个值(getnext和kmp函数都利用了)

由于本人太菜,不能写出那样的代码,所以还是将设为0吧

for(int i=0; i < rstr_len; ++i) {
    if(sstr_index == sstr_len) {
    	// 找到了
        sstr_index = 0;
    }
    
    if(rstr[i] == sstr[sstr_index]) {
        ++sstr_index;
    }else {
	    // next 可能会返回 next[0] 的值,如果是-1那么就麻烦了,或者预防一下
	    // 但是,也可以将 next[0] 的值设为 0
        sstr_index = next[sstr_index];
    }
}

如有错误请指出?

markdown 用的好烂!!!

发布了36 篇原创文章 · 获赞 1 · 访问量 8992

猜你喜欢

转载自blog.csdn.net/u011091701/article/details/95937934