初探KMP算法之next/nextval

初探KMP算法之next/nextval


一、何为KMP算法——模式匹配的一种改进算法

        这种算法是D.E.Knuth与V.R.Pratt和J.H.Morris同时发现的,因此人们称它为克努特—莫里斯—普拉特算法(简称为KMP算法)。此算法可以在O(m+n)的时间数量级上完成串的模式匹配操作。

        其改进在于:每当一趟匹配过程中出现字符比较不等时,不需要回溯 i 指针,而是利用已经得到的“部分匹配”的结果将模式向右“滑动”尽可能远的距离后,继续进行比较。


二、next函数值计算

        模式串的next函数定义:


由此定义可推出下列模式串的next函数值:


计算next总原则(非常重要):

      计算next[j]函数值,必从j-1位置开始(不是j),以j-1为节点号,next[j-1]为链表尾指针,一直往前查找,直到找到与arr[j-1]值相同的位置时(k)停止,则此时next[j]=next[k]+1。(注:将上述表格抽象为链表,从后往前的链表)

  • 务必准确把握——j、模式串、next[j]的不同含义,不要搞混了。
几大注意事项:

        a)next[0]一般不使用,直接将next[1]=0,next[2]=1,j从1开始,j和next[j]有一一对应关系;

                                                                              (注:如果非要使用next[0]的话,则置next[1]=-1,next[2]=0)
        b)一个特殊状态,如果按照上述总原则,无法找到与arr[j-1]值相同的位置。则一律置next[j]=1。

为了形式化,首先,将上述表格转化为双亲表示法树状结构如下:



                                                                                                                                                                                            

示例next[5],求法;

  • 表格求解法如下:


  • 双亲节点表示法,求解过程如下:


                                                                                                                                                                                       

示例next[3],求法;



以下留下几个表格,供手动模拟练习一下计算过程:




三、nextval函数值计算

        前面定义的next函数在某些情况下尚有缺陷(进行了某些不必要的匹配,如下图)。为了解决该问题引入了nextval对next进行修正。


接下来继续使用上述,问题为材料对next进行修正:


在计算出next之后,得出nextval十分之简单,用以下代码表示更直接:

if (arr[j]==arr[next[j]]){
			next[j] = next[next[j]];
		}

就是判断arr[j]与其next[j]所对应的arr[next[j]]是否相同,相同则修正,否则不需要修正。

附next/nextval测试:
#include <stdio.h>

int next[9];
//不修正
int getNextj(char arr[], int j){
	
	int k = j-1;

	while (k > 0){
		//③比较arr[j-1]是否等于arr[next[j-1]]
		if (arr[j-1]==arr[next[k]]){
			return next[k]+1;
		}
		//④不等于,继续找下去
		k = next[k];
	}
	//⑤未找到,恒置1
	return 1;
	
}

//修正
void getNextVal(char arr[]){

	int j;
	for (j = 1; j < 9; ++j){
		//⑥修正,如果arr[j]==arr[next[j]]则修正
		if (arr[j]==arr[next[j]]){
			next[j] = next[next[j]];
		}
	}
	
}

int main(void){
	
	char arr[] = " abaabcac";//前面有一空格,T[]从1开始
	int j;
	//①初始化
	next[1] = 0;
	next[2] = 1;
	//②逐个求next
	for ( j = 3; j <= 8; ++j ){
		next[j] = getNextj(arr,j);
	}
	
	//打印
	printf("----next---\n");
	for ( j = 1; j <= 8 ;++j){
		printf("%d ",next[j]);
	}

	//修正
	getNextVal(arr);
	printf("\n----nextval---\n");
	for ( j = 1; j <= 8 ;++j){
		printf("%d ",next[j]);
	}
	printf("\n");
	
	return 0;
}
----next---
0 1 1 2 2 3 1 2
----nextval---
0 1 0 2 1 3 0 2
Press any key to continue

Copyright © 2017 KINGDOM. All rights reserved.

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值