初探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]];
}
#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.