KMP算法 → 计算nextval数组

KMP算法中的nextval数组是对next数组的优化,减少了无意义的匹配比较。手动求nextval数组分为从1开始和从0开始两种情况,通过比较模式串的字符与next数组对应位置的字符是否相等来确定nextval值。
摘要由CSDN通过智能技术生成

【算法解析】
● 众所周知,KMP算法中模式串T的next数组,是KMP算法的核心。
next数组的核心作用是“
当模式串T的第j位与主串S的第pos位失配时(即 T[j]≠S[pos] 时),让模式串T的第next[j]位与主串S的第pos位再进行比较”。这相当于让模式串T往右移动了 j-next[j] 位后,再进行比较。

● 正是由于next数组的引入,使得KMP算法的效率大大优于BF算法。但是,next数组在某些情况下仍存在缺陷。
例如,针对模式串"aaaab",其相应的next数组值为“-1 0 1 2 3”。则据next数组的作用易知,当 T[3]=a 与主串 S[pos] 失配时,则会使用 T[next[3]]=T[2]=a 与主串 S[pos] 再进行匹配,显然还会失配。这是因为,T[0]~T[2]与T[3]都相等,当T[3]与S[pos]失配时,T[0]~T[2]与S[pos]必然也失配。显然T[0]~T[2]与S[pos]的比较
没有意义,一定会匹配失败。针对本模式串"aaaab"而言,将会有 T[3]=a≠S[pos],T[next[3]]=T[2]=a≠S[pos],T[next[2]]=T[1]=a≠S[pos],T[next[1]]=T[0]=a≠S[pos] 等四次比较。

● nextval数组的引入,正是为了减少这种无意义的比对,而对next数组进行的优化

 


【手动求nextval数组的步骤】
● 手动求nextval数组(模式串下标从1开始)
若在KMP算法设计中,将模式串下标从1开始计数,那么求nextval数组的算法步骤为:
(1)求出next数组的值(定义next[1]=0,next[2]=1);
(2)定义nextval[1]=0。然后,比较模式串的第j个
(j>1)字符是否与第next[j]个字符相等。若相等,则模式串第j个字符的nextval值等于第next[j]个字符的nextval值。若不等,则模式串第j个字符的nextval值等于其next数组值。
即,若T[j]=T[next[j]],则nextval[j]=nextval[next[j]]。否则,nextval[j]=next[j]。

● 手动求nextval数组(模式串下标从0开始)
若在KMP算法设计中,将模式串下标从0开始计数,那么求nextval数组的算法步骤为:
(1)求出next数组的值(定义next[0]=-1,next[1]=0);
(2)定义nextval[0]=-1。然后,比较模式串的第j个
(j>0)字符是否与第next[j]个字符相等。若相等,则模式串第j个字符的nextval值等于第next[j]个字符的nextval值。若不等,则模式串第j个字符的nextval值等于其next数组值。
即,若T[j]=T[next[j]],则nextval[j]=nextval[next[j]]。否则,nextval[j]=next[j]。

【求nextval数组的算法代码】

#include<iostream>
using namespace std;

const int maxn=100;
int ne[maxn],nev[maxn];

void getNext(string s) {
	int len=s.length();
	int i=0,j=-1;
	ne[0]=-1;
	while(i<len) {
		if(j==-1||s[i]==s[j]) {
			i++;
			j++;
			ne[i]=j;
		} else j=ne[j];
	}
}

void getNextval(string s) {
	int len=s.length();
	int i=0,j=-1;
	nev[0]=-1;
	while(i<len) {
		if(j==-1||s[i]==s[j]) {
			i++;
			j++;
			nev[i]=j;
			if(s[i]!=s[j]) nev[i]=j;
			else nev[i]=nev[j];
		} else j=nev[j];
	}
}

int main() {
	string T;
	getline(cin,T);

	getNext(T);
	for(int i=0; i<T.length(); i++) {
		cout<<ne[i]<<" ";
	}

	cout<<endl;

	getNextval(T);
	for(int i=0; i<T.length(); i++) {
		cout<<nev[i]<<" ";
	}

	return 0;
}


/*
input:
abcaabbabcab

output:
-1 0 0 0 1 1 2 0 1 2 3 4
-1 0 0 -1 1 0 2 -1 0 0 -1 4
*/


【编程技巧】
● 由于字符串的下标从0开始,因此采用李春葆《数据结构》中的方式定义nextval[0]=-1,nextval[1]=0是很自然的事情。之后,利用语句 cout<<nextval[i]<<" "; 便可输出以“-1 0 ”开头的nextval数组值。
● 若想输出以“0 1”开头的nextval数组值,只需修改语句cout<<nextval[i]<<" "; 为
cout<<nextval[i]+1<<" "; 便可。其他代码保持不变。


【参考文献】
https://blog.csdn.net/qq_43456605/article/details/119954614
https://blog.csdn.net/qq_35963993/article/details/106236665



 

  • 13
    点赞
  • 76
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值