KMP算法中next与nextval的实现原理

KMP算法中next与nextval的实现原理

最近学习KMP算法,在next数组以及nextval数组的理解上下了好大的功夫,才得以理解。
仅作为备忘以及参考作用,在此记录下理解过程。如有错漏指出,望斧正!

KMP算法的实现以及next,nextval数组的计算我就不加赘述了,以下解释个人对两个数组的理解。

前提信息

next,nextval数组是为了实现不同于朴素模式匹配算法(Brute-Force算法) 的KMP算法而添加的,其中原版KMP使用的是next数组,然而,在某些情况下(在各大牛的KMP算法介绍里应该有提到,个人认为是一些额外的运算浪费)next数组仍会出现不足之处,因此,人们就设计了nextval数组进行优化。
在以下文字中,我将使用:
主串:将在其中进行搜索的字符串。
模式串:待搜索的字符串。
即,对于master = “ababad”, mode = "baba"两个字符串,
在master中匹配mode,此时master为主串,mode为模式串。

next数组

在这里,我将假设你已经理解什么是朴素模式匹配算法,并且明白了其中原理。
next数组,又被称作next函数、覆盖函数等等,是为了以
主串不动,移动模式串的方法来实现时间复杂度的优化(O(mn) --> O(m+n))
继续使用以上的两个字符串,master = “ababacbabad”, mode = “babad”,在master中匹配mode。
对于模式串 babad,它的next数组如下

mode b a b a d
next -1 0 0 1 2

含义即,当在j处匹配不上时,将模式串指针指到next[j]处,
参考下图:
在这里插入图片描述

nextval数组

对于模式串 babad,它的next数组如下

mode b a b a d
next -1 0 0 1 2

它的nextval数组如下

mode b a b a d
nextval -1 0 -1 0 2

二者唯一的区别就是,若某一位next值对应的字符这一位字符相同,那么这一位nextval的值则为这一位的next值对应的next值
形式化表达:

	if(mode[j] == mode[next[j]]){
		nextval[j] = nextval[next[j]];
		//因为迭代是从左到右进行的,此时nextval[next[j]]已经按此原则完成赋值
	}
按此原则实现的原因

以上述模式串mode第三位mode[2] ‘b’ 为例,假设有一主串“bacad”,
此时显然在 j = 2处匹配失败,

按照next数组的操作方法:
①读取next[2] = 0;
②移动mode,将第0位与j对齐,进行匹配
③mode[next[2]] == mode[0] == ‘b’,重蹈覆辙
④读取next[0] == -1;
⑤移动mode,将第-1位与i对齐。

按照nextval的操作方法:
①读取nextval[2] = next[next[2]] = -1;
②移动mode,将第-1位与i对齐。

这便是使用nextval减少额外运算的智策。

附上KMP算法代码以供参考

#include <iostream> 
#include <string> 
#include <iomanip>
using namespace std; 

void Cal_next(int *next, string mode, int len) {
	//next[i]含义为若在i处匹配失败时,i应该跳转至的模式串中的位置
    int i, j;
    j = -1;
    i = 0;
    next[0] = -1;
    while(i<len-1){
        if(j==-1||mode[i] == mode[j]){
            i++;
            j++;
            next[i] = j;
        }
        else{
            j = next[j];
        }
    }
    //print:
    cout<<"[";
    for(int t = 0;t<len;t++){
        cout<<setw(2)<<next[t]<<" ";
    }
    cout<<"]"<<endl;
}

void Cal_nextval(int *nextval, string mode, int len) {
	//nextval[i]含义为若在i处匹配失败时,i应该跳转至的模式串中的位置
    int i, j;
    j = -1;
    i = 0;
    nextval[0] = -1;
    while(i<len-1){
        if(j==-1||mode[i] == mode[j]){
            i++;
            j++;
            if(mode[i]==mode[j]){
                nextval[i] = nextval[j];
            }
            else{
                nextval[i] = j;
            }
        }
        else{
            j = nextval[j];
        }
    }
    //print:
    cout<<"[";
    for(int t = 0;t<len;t++){
        cout<<setw(2)<<nextval[t]<<" ";
    }
    cout<<"]"<<endl;
}


int KMP_nextval(string master, string mode) {
    int l1 = master.length(); 
    int l2 = mode.length(); 
    int * next = new int[l2+1];
    int pos = -1;
    Cal_next(next, mode, l2);
	//int * nextval = new int[l2+1];
    //cout<<endl;
    //Cal_nextval(nextval, mode, l2);
    int i = 0, j = 0;
    while(i<l1){
        if(j==-1||master[i]==mode[j]){
            i++;
            j++;
            if(j>=l2){
                pos = i - l2;
                break;
            }
        }
        else{
            j = next[j];
        }
    }
    return pos;
}

int KMP_next(string master, string mode) {
    int l1 = master.length(); 
    int l2 = mode.length(); 
    int pos = -1;
	int * nextval = new int[l2+1];
    Cal_nextval(nextval, mode, l2);
    int i = 0, j = 0;
    while(i<l1){
        if(j==-1||master[i]==mode[j]){
            i++;
            j++;
            if(j>=l2){
                pos = i - l2;
                break;
            }
        }
        else{
            j = nextval[j];
        }
    }
    return pos;
}

希望以上内容能为看到这里的你们提供一些帮助!如有错漏指出,望斧正!
//Szp 2018.10.26

发布了30 篇原创文章 · 获赞 7 · 访问量 4435

猜你喜欢

转载自blog.csdn.net/weixin_40005329/article/details/83413630