KMP算法

#include<stdio.h>
#include<malloc.h>
#include<string.h>


//结构的定义字符串
typedef struct {


char *str;//字符串
int maxLength;//最大能够存放字符的长度
int length;//眼下的字符长度
}DString;




//1.初始化操作
//初始化操作用来建立和存储串的动态数组空间以及给相关的数据域赋值
void Initiate(DString *s,int max,char *string){


int i;
s->str=(char *)malloc(sizeof(char)*max);//申请动态存储空间
s->maxLength=max;//动态数组元素的最大的个数
s->length=strlen(string);//置串的当前长度
for(i=0;i<s->length;i++){//进行for循环赋值

s->str[i]=string[i];//赋值
}
}




//2.插入子串操作
int Insert(DString *s,int pos,DString t){
//在主串s的pos位置插入子串t,插入成功返回1,失败返回0
int i;
if(pos<0){

printf("參数pos的位置出错,pos<0 ");
return 0;
}else{
//假设空间不够,则进行从新分配空间
if(s->length+t.length>s->maxLength){

//又一次申请s->str所指的数组空间,原数组元素存放在新数组的前面
realloc(s->str,(s->length+t.length)*sizeof(char));
s->maxLength=s->length+t.length;//最大的长度变了
}


for(i=s->length-1;i>=pos;i--){
//依次往后移动t.length个位置
s->str[i+t.length]=s->str[i];
}


//在pos位置进行插入操作
for(i=0;i<t.length;i++){

s->str[pos+i]=t.str[i];//插入


}


s->length=s->length+t.length;//置新的数据元素的位置
return 1;
}
}






//3.删除子串操作
int Delete(DString *s,int pos,int len){
//删除主串s从pos位置開始的长度为len的子串,删除成功则返回1,失败则返回0
int i;
if(s->length<=0){
printf("数组中未存放字符无元素可删! ");
return 0;
}else if(pos<0||len<0||pos+len>s->length){

printf("參数pos和len不合法! ");
return 0;
}else{

for(i=pos+len;i<=s->length-1;i++){

s->str[i-len]=s->str[i];//依次前移len歌位置
}


s->length=s->length-len;//置新的元素的个数
return 1;
}




}






//4.取子串操作
int SubString(DString *s,int pos,int len,DString *t){
//取主串从pos位置開始的长度为len的子串,取成功则返回1,失败则返回0
int i;
if(pos<0||len<0||pos+len>s->length){

printf("參数pos和len的位置出错!!! ");
return 0;
}
//当t的空间不够的时候,在进行从新分配空间
if(len>t->maxLength){

t->str=(char *)realloc(t->str,len*sizeof(char));//又一次申请数组空间
t->maxLength=len;
}


for(i=0;i<len;i++){

t->str[i]=s->str[pos+i];//取子串
}


t->length=len;
return 1;


}






//5.撤销操作
void Destroy(DString *s){


//撤销串s占用的内存空间
free(s->str);
s->maxLength=0;
s->length=0;
}








//Brute-Force算法函数的设计
//i变量指示主串s当前比較字符的下标
//用j变量表示子串t当前比較字符的下标
int BFIndex(DString s,int start,DString t){


int i=start,j=0,v;
while(i<s.length&&j<t.length){

if(s.str[i]==t.str[j]){

i++;
j++;
}else{

i=i-j+1;
j=0;
}


}


if(j==t.length){

v=i-t.length;


}else{

v=-1;
}


return v;


}


/*
next[j]  (1):max{k|0<k<j且"t0t1t2....t(k-1)"="t(j-k)t(j-k+1).....t(j-1)"}     当此集合非空时
          (2):0   其它的情况  不存在不论什么满足"t0t1t2...t(k-1)"="t(j-k)(j-k+1)....t(j-1)"条件的真子串
 (3):-1      j=0;




next[j]值得函数的设计
1.若tk=tj,则表明在模式串t中有"t0t1....tk"="t(j-k)t(j-k+1)....tj",且不可能存在不论什么一个k'>k
满足上式。因此有:next[j+1]=next[j]+1=k+1




  
2.若tk!=tj,则可把计算next[j+1]值转换为,把模式串t'向右滑动至k'=next[k](0<k'<k<j).
t'="t0t1t2....t(k'-1)t(k')"
若此时有"t0t1...t(k')"="t(j-k')t(j-k+1)....tj"(0<k'<k<j),因此有
 next[j+1]=k'+1=next[k]+1


若此时t(k')!=tj,则将模式串t'右滑到k''=next[k'],其余类推,直到某次比較有tk=tj,
或某次比較有tk!=tj且k=0,此时:next[j+1]=0




*/
//kmp算法的求子串的next[j]值得算法例如以下
void GextNext(DString t,int next[]){


//求子串t的next[j]值并存放在数组next中
int j=1,k=0;
next[0]=-1;
next[1]=0;
while(j<t.length-1){

if(t.str[j]==t.str[k]){

next[j+1]=k+1;
j++;
k++;
}else if(k==0){

next[j+1]=0;
j++;
}else{

k=next[k];
}
}




}


//kmp算法的函数实现
int KMPIndex(DString s,int start,DString t,int next[]){
//查找主串s从start始的子串t成功。则返回t在s中的首字符的位置,失败,则返回-1
//数组next中存放有模式串t的next[j]值
int i=start,j=0,v;


while(i<s.length&&j<t.length){

if(s.str[i]==t.str[j]){//相等则继续比較下一个字符

i++;
j++;
}else if(j==0){//假设j=0,且s.str[i]!=t.str[j],则主串依到下一个自发进行比較

i++;
}else{

j=next[j];//除了上面的情况外,子串须要进行右移进行接下来的比較
}
}


if(j==t.length){

v=i-t.length;
}else{

v=-1;
}


return v;


}




int main(){
/*
DString myString1,myString2,myString3;
int i,max1=5,max2=9,max3=0;
//測试初始化函数
Initiate(&myString1,max1,"Data");
Initiate(&myString2,max2," Structure");
Initiate(&myString3,max3,"");


printf("初始化myString2串:        ");
for(i=0;i<myString2.length;i++)
printf("%c",myString2.str[i]);
printf("    maxLength=%d",myString2.maxLength);
    printf("    length=%d ",myString2.length);


//測试插入函数
Insert(&myString2,0,myString1);
printf("插入子串后myString2串:  ");
for(i=0;i<myString2.length;i++)
printf("%c",myString2.str[i]);
printf("    maxLength=%d",myString2.maxLength);
    printf("    length=%d ",myString2.length);


//測试删除函数
Delete(&myString2,0,5);
    printf("删除子串后myString2串:  ");
for(i=0;i<myString2.length;i++)
printf("%c",myString2.str[i]);
printf("    maxLength=%d",myString2.maxLength);
    printf("    length=%d ",myString2.length);




//測试取子串函数
SubString(&myString2,0,5,&myString3);
printf("取子串后myString3串:  ");
for(i=0;i<myString3.length;i++)
printf("%c",myString3.str[i]);
printf("    maxLength=%d",myString3.maxLength);
    printf("    length=%d ",myString3.length);






////////////////////////////
    for(i=0;i<myString2.length;i++)
printf("%c",myString2.str[i]);
printf("    maxLength=%d",myString2.maxLength);
    printf("    length=%d ",myString2.length);




//測试撤销函数
//Destroy(&myString1);
    //Destroy(&myString2);
//Destroy(&myString3);
*/






/*
    //broute-force查找
DString myString1,myString2;
int max1=29,max2=9;
int pos=0;
Initiate(&myString1,max1,"Data Structure Data Structure");
    Initiate(&myString2,max2,"Structure");
//第一次查找
pos=BFIndex(myString1,pos,myString2);
printf("第一次查找时 pos=%d ",pos);


//第二次查找
pos=BFIndex(myString1,pos+1,myString2);
printf("第二次查找时 pos=%d ",pos);
*/










DString myString1,myString2;
int max1=29,max2=9;
int pos=0;
    int next[29];
Initiate(&myString1,max1,"Data Structure Data Structure");
    Initiate(&myString2,max2,"Structure");
    GextNext(myString2,next);
pos=KMPIndex(myString1,pos,myString2,next);
    printf("pos=%d ",pos);
pos=KMPIndex(myString1,pos+1,myString2,next);
    printf("pos=%d ",pos);
return 0;
}




/*
s为主串。t为模式串,设i为主串s当前比較字符的下标,j为模式串t当前比較的下标,






1.模式串的真子串
设s="s1s2s3s4.....s(n-1)",t="t0t1t2t3t4.....t(n-1)"当模式串匹配到si!=tj(0<=i<n,0<=j<m)
则必然存在:
"s(i-j)s(i-j+1)...s(i-1)"="t0t1t2t3t4....t(j-1)"


2.若模式串中存在可相互重叠的真子串。满足
"t0t1t2...t(k-1)"="t(j-k)(j-k+1)....t(j-1)"      (0<k<j)


3.next[j]函数
 next[j]  (1):max{k|0<k<j且"t0t1t2....t(k-1)"="t(j-k)t(j-k+1).....t(j-1)"}     当此集合非空时
          (2):0   其它的情况  不存在不论什么满足"t0t1t2...t(k-1)"="t(j-k)(j-k+1)....t(j-1)"条件的真子串
 (3):-1      j=0;


kmp算法的思想是:当子串t中的tj与主串的si(i>=j)比較不相等的时候。若子串t中部存在如上所说的真子串,
有next[j]=0,则下一次比較si和t0,这是第一种情况;
若模式串t中存在真子串"t0t1t2...t(k-1)"="t(j-k)(j-k+1)....t(j-1)"      (0<k<j)则有next[j]=k,
next[j]=k表示的是随后和主串s的si比較的子串t的字符为tk,这是另外一种情况;当j=0时,令next[j]=-1(此处-1为一标志)
,此时令主串和子串的下标个增1,随后比較s(i+1)和t0


*/


注意:

1,在普通情况下,brute-force算法和kmp算法的比較次数或者时间效率很接近

例;s="cddcdc"    t="abcde"

2.当出现brute-force算法最好的情况时,kmp算法的比較次数要比brute-force算法的比較次数少非常多

例:s="aaaaaaaa"     t="aab"

3.在大多少的情况下kmp算法的时间效率要高于brute-force算法的时间效率

4.kmp比較次数多的情况,主要在模式串多次右滑。最后仍不相等时发生





















版权声明:本文博主原创文章,博客,未经同意不得转载。

原文地址:https://www.cnblogs.com/lcchuguo/p/4826859.html