最近老被KMP 算法给烦着,几经思考加探索加画图加验证加分析,终于在我的努力下,发现了书中一个重大的问题,它里面的KMP函数是化简了的,也就是说书上的解释 与 函数 是不完全对应的,这可苦了那些绞尽脑汁苦苦思索的学生啊!
经我仔细考究,它的解释基本没问题,个别地方还是强调一下比较好。但是为什么只把化到非常简的函数放上去,而不把按照解释写出来的函数放上去呢?莫非作者偷懒?
不管怎么说,作者这样做是非常不合理的,写出来的解释与实际使用的函数不一致,虽然结果一样,但是学生的思维也会跟着走乱了。
我看到网上的代码大都是化简后的,依我看,十有八九都是看不懂的。
本来想把我依照书上说的写出来的代码弄上来,不过由于时间关系还是等下次吧!
任何代码都是由繁琐到精练的,企图一下子写出最精练的代码,完全是违背常理的。
最高效的代码未必容易看懂,还是从基本的开始吧!
———————————————————————————————————————————————
2009年2月20日 星期五 一个倒春寒的早上,经过一夜的思索,我终于把kmp的代码搞定了。
PS:这件事情自上次发表这篇文章,已经过去了几个月了,这段时间以来由于学业任务,一直没有时间研究这个问题。昨晚想了一下,没想出,今早又考虑了一段时间,得到了一些灵感,也把代码上的bug修正了,当然,这里指的是我的代码,书上的代码是没问题的,不过就是觉得它与书上的分析不能很好对应,是写给机器看的吧????
好了,这里把我的代码发布一下,仅供参考,由于本人水平有限,希望各位能够不吝赐教。
以下代码都以输出next元素的值为目的。
一、先是书上的代码(为了方便显示next的值,我删减改动了部分代码):
//用KMP算法对主串和模式串进行模式匹配。本题目给出部分代码,请补全内容。
#include “stdio.h”
#include “stdlib.h”
#include “iostream.h”
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASLBLE -1
#define OVERFLOW -2
#define MAXSTRLEN 255 //用户可在255以内定义最大串长
typedef unsigned char SString[MAXSTRLEN+1]; //0号单元存放串的长度
void get_next(SString T,int next[])
{
// 算法4.7
// 求模式串T的next函数值并存入数组next
// 请补全代码
int i,j;
i = 1;
next[1] = 0 ;
j = 0;
while( i < T[0]-‘0’ )
{
if(j == 0 || T[i] == T[j])
{
++i;
++j;
//if(T[i] != T[j])//这里去掉注释后就变成了书上的改进算法
next[i] = j;
//else next[i] = next[j];//这里去掉注释后就变成了书上的改进算法
}
else j = next[j];
}
}
int main()
{
SString S;
SString T = {‘8′,’a’,’a’,’c’,’a’,’b’,’a’,’a’,’c’};//,’c’};
//T[0]=i-1; // T[0]用于存储模式串中字符个数
//是char型啊!!!
int i,j,n,k = 0,posArray[100],l;
char ch;
int pos = 0; //pos!!!!!!!!要求模式串从pos个位置后开始匹配
int next[100];
get_next(T,next);
for(i = 1; i<=T[0]-‘0’;i++)
printf(“%d “,next[i]);
return 0;
}
二、这是我写的kmp非递归算法(都在一个main里):
#include <stdio.h>
#define LEN 6
int main ()//这里应用的是分情况处理的思想
{
int j,k,i;
int next[LEN];
int flag = 0;//设标记是为了区分不同情况
char ch[LEN] = {‘0′,’a’,’a’,’a’,’a’,’b’};
next[1]=0;
for (j=1;j<LEN;j++)
{
k = next[j];
if(k>=2)
{
do {
if(ch[k]==ch[j])
{
if(flag==0)
{
{}
next[j+1] = next[j]+1;
}
if(flag==1)
{
{}
next[j+1] = next[k]+1;
flag = 0;
}
}
else {
k = next[k];
if(k==1||k==0)
break;
flag = 1;
}
}while(ch[k] != ch[j]);
}
if(k == 0)
next[j+1] = 1;
if(k == 1)
{
{}
if(ch[j] == ch[1])
next[j+1] = 2;
else next[j+1] = 1;
}
}
for(i=1;i<LEN;i++)
{
printf(“%d “,next[i]);
}
return 0;
}
三、这是我写的kmp非递归算法的改进版:
#include <stdio.h>
#define LEN 9
void validateNext(char ch[] , int next[],int len)
{
int i;
for(i = 2; i<=len;i++)//begin from 2
{
if(ch[i]==ch[ next[i] ])
next[i] = next[ next[i] ];
}
}
void getNext (char ch[],int next[],int len )
{
int j,k,i;
//int next[LEN];
int flag = 0;
//char ch[LEN] = {‘0′,’a’,’a’,’a’,’a’,’b’};
next[1]=0;
for (j=1;j<LEN;j++)
{
k = next[j];
if(k>=2)
{
do {
if(ch[k]==ch[j])
{
if(flag==0)
{
{}
next[j+1] = next[j]+1;
}
if(flag==1)
{
{}
next[j+1] = next[k]+1;
flag = 0;
}
}
else {
k = next[k];
if(k==1||k==0)
break;
flag = 1;
}
}while(ch[k] != ch[j]);
}
if(k == 0)
next[j+1] = 1;
if(k == 1)
{
{}
if(ch[j] == ch[1])
next[j+1] = 2;
else next[j+1] = 1;
}
}
// return 0;
}
int main()
{
int len,i;
int next[LEN];
//int flag = 0;
char ch[LEN] = {‘0′,’a’,’a’,’c’,’a’,’b’,’a’,’a’,’c’};
len = LEN ;
getNext(ch,next,len);
validateNext(ch,next,len);
for(i = 1;i<len;i++)
printf(“%d “,next[i]);
return 0;
}
四、这是我写的kmp递归算法:
#include <stdio.h>
#define LEN 9
//这要最终有return,就不怕陷入无限循环,大胆地递归吧!!!!
int getNext(char ch[] , int next[],int n)//n means the Number of next[]’s element
{
int j = n-1;
int k;
if(j != 0)//out of recursion
k = getNext(ch,next,j);
if( n == 1)
{
next[1] = 0;
return next[1];
}
if(n == 2)
{
next[2] = 1;
return next[2];
}
if(ch[k]==ch[j])
next[j+1] = next[j] +1;
else
{
k = getNext(ch,next,k);//recursion until ch[k]==ch[j]
next[j+1] = next[k] + 1; //
}
return next[j+1];
}
int main ()
{
int j,k,i;
int next[LEN];
int flag = 0;
char ch[LEN] = {‘0′,’a’,’a’,’c’,’a’,’b’,’a’,’a’,’c’};
//next[1]=0;
int len = LEN – 1;
getNext(ch,next,len);
for(i=1;i<LEN;i++)
{
printf(“%d “,next[i]);
}
return 0;
}
五、这是我写的kmp递归算法的改进版:
#include <stdio.h>
#define LEN 9
int getNext(char ch[] , int next[],int n)//n means the Number of next[]’s element
{
int j = n-1;
int k;
if(j != 0)//out of recursion
k = getNext(ch,next,j);
if( n == 1)
{
next[1] = 0;
return next[1];
}
if(n == 2)
{
next[2] = 1;
return next[2];
}
if(ch[k]==ch[j])
next[j+1] = next[j] +1;
else
{
k = getNext(ch,next,k);//recursion until ch[k]==ch[j]
next[j+1] = next[k] + 1; //
}
return next[j+1];
}
void validateNext(char ch[] , int next[],int len)
{
int i;
for(i = 2; i<=len;i++)//begin from 2
{
if(ch[i]==ch[ next[i] ])
next[i] = next[ next[i] ];
}
}
int main ()
{
int j,k,i;
int next[LEN];
int flag = 0;
char ch[LEN] = {‘0′,’a’,’a’,’c’,’a’,’b’,’a’,’a’,’c’};
//next[1]=0;
int len = LEN – 1;
getNext(ch,next,len);
validateNext(ch,next,len);
for(i=1;i<LEN;i++)
{
printf(“%d “,next[i]);
}
return 0;
}
注:留心的朋友应该会注意到,这里面多次使用了validateNext()函数,这一点很关键,一开始我总是试图直接在getNext()的基础上改进,后来发觉很困难,于是产生了分成两步完成的想法,一步先算出基础值,另一步再优化值,于是所有问题就迎刃而解了。这一点想法让我兴奋了很久。。。。。。YY咯。。。。。。。
PS:这里我用了validate这个单词不过后来查了一下,好像不合适,课本里用val,意思就是“修正”吧?
不过我查不到val的完整写法,自己还莫名其妙地用了validate,大家将就一下好了。。。。。。
———————————————————————————————————————————————
人活着,必定是为了思考。
我在思考中才能感知我的存在。
收藏于 2008-10-24