数据结构————KMP算法

KMP算法相比于BF算法,是一种用空间换时间的算法,比BF的普通算法效率高很多。

BF算法需要将源字符串中的每一个字符与模式串中字符比较至少一次,并且会有大量回溯的时间,下面我们来分析一下BF算法的时间复杂度

我们假设源字符串的长度为n模式串的长度为m,假设从源字符串的第i个位置开始于模式匹配成功。BF算法的时间复杂度分两种情况,第一种是最好情况源字符串中匹配位置之前的每一个字符都与模式串的第一个字符不一样第二种是最坏情况即源字符串中匹配位置之前的每一组都只与模式串中的最后一个字符不一样。

1.最好情况:前i-1趟匹配中字符总共匹配了i-1次,设源字符串中起始位置由1到n-m+1上的匹配成功概率相等即为1/(n-m+1),则平均比较次数为:

\sum_{i=1}^{n-m+1} Pi(i-1+m)=\frac{1}{n-m+1}\sum_{i=1}^{n-m+1}i-1+m=\frac{n+m}{2}

2.最坏情况:前i-1趟匹配中字符总共比较了(i-1)*m次,则加上最后一次的成功匹配总比较次数是i*m次,则平均比较次数为: 

\sum_{i=1}^{n-m+1}Pi(i*m)=\frac{1}{n-m+1}\sum_{i=1}^{n-m+1}i*m=\frac{(n-m+2)*m}{2}

则我们可以看出,BF算法在最坏情况下的时间复杂度是O(n*m),在最好情况下的时间复杂度是O(n+m)。那么我们介绍一种时间复杂度更小的算法KMP算法。


下面我们通过图解来理解KMP算法:

假设这里有  源字符串ABDCE ABCABD AABDAB  和  模式串ABDAB  进行比较

在此之前,我们先进行一个操作算next数组,这个数组如何用在哪里用我们后面再说。计算方法是模式串中每一个元素与它之前的元素中前缀表达式和后缀表达式一样的最大个数。什么是前缀表达式和后缀表达式呢?

比如bread这个字符串,它的前缀表达式包括:b,br,bre,brea;后缀表达式包括:d,ad,ead,read。所以前缀表达式就是除去最后一个,后缀表达式就是除去第一个,我想这个例子会很浅显易懂。

所以在模式串ABDEF中,其next的值依次为

字符串移动的位数=已匹配的个数-next值


 

下面我们主要来分析一下过程:

1.源字符串与模式串中A字符一样,那么就都比较下一个

2.源字符串B和D与模式串中一样,比较下一个

 

3.此时模式串的A与源字符串的C不同,那么D对应的是next[2],next[2]=0,所以字符串整体向后移动位数=已匹配个数-next值=3-0=3

 4.移动后C与A不相等,如果源字符串与模式串第一个元素就不相等的话,就将模式串向后移动一位,直到我们移动到A后。

 

 5.此时比较并移动后发现A与A相等,B与B相等,但C与D不相等,那么又要移动模式串,移动位数=已匹配个数-next值=2-0=2

 

 6.移动后源字符串与模式串的第一个不相等,那么模式串往后移动一位。

 7.这时发现A与A相等,那么我们将模式串与源字符串继续向后比较,直到源字符串的空格与模式串中A不匹配,则移动模式串,移动位数=已匹配个数-next值=3-0=3

 8.移动3位后发现空格与A不匹配,那么再往后移动模式串一位

 9.此时A与A相等,但再往后匹配后A与B又不相等,所以再次移动模式串,移动位数=已匹配个数-next值=1-0=1

 10.移动后再依次进行匹配,就发现完全可以匹配,此时返回该位置

 


理解了该过程之后,我们来实现这个代码:

(头文件)

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
typedef struct{
    char*data;
    int size;
    int capacity;
}ST;
void InitString(ST*ps);//初始化
void InsertString(ST*ps);//插入元素
void PrintString(ST*ps);//打印串
void GetNext(ST*S,ST*T);//获取next值
void KMP(ST*S,ST*T);//KMP算法实现

(源文件1)

void InitString(ST*ps){
    ps->data=NULL;
    ps->size=0;
    ps->capaicty=0;
}
void checkcapacity(ST*ps){
    if(ps->capacity==ps->size){
        int newcapacity=ps->capacity==0?4:ps->capacity*2;
        char*tmp=(char*)realloc(ps->data,ps->capacity*2*sizeof(char));
        if(tmp==NULL)
            exit(0);
        else{
            ps->data=tmp;
            ps->capcity=newcapacity;
        }
    }
}
void InsertString(ST*S,ST*T){
    char ch;
    printf("请输入源字符串:");
    while((ch=getchar()!='\n'){
        checkcapacity(S);
        S->data[S->size++]=ch;    
        S->data[S->size]='\0';
    }
    printf("请输入模式串:");
    while((ch=getchar()!='\n'){
        checkcapacity(T);
        T->data[S->size++]=ch;
        T->data[S->size]='\0';
    }
}
void PrintString(ST*ps){
    int i;
    for(i=0;i<ps->size;i++)
        printf("%c ",ps->data[i]);
}
void GetNext(ST*T,int*next){
    char*head,*tail;//设定两个字符串
    head=(char*)malloc(sizeof(char));
    tail=(char*)malloc(sizeof(char));//动态开辟空间
    if(!head&&!tail)
        exit(0);
    for(int i=1;i<T->size;i++){
        j=0;
        while(T->data[j]!='\0'){
            head[j]=T->data[j];
            tail[j]=T->data[j];
            j++;
            head[j]='\0';
            tail[j]='\0';//将模式串所有内容复制到俩个字符串中
        }
        head[i]='\0';
        tail[i+1]='\0';//设置\0以方便之后使用string函数
        for(j=0;j<i;j++){
            if(strcmp(head,tail+1+j)==0){//比较两个字符串
                next[j]=strlen(head);
                break;
            }
            int temp=strlen(head)-1;
            head[temp]='\0';
        }
    }
}

(源文件2)

 

void KMP(ST*S,ST*T,int *next){
    int i=0,j=0;
    while(i<S->size){
        if(S->data[i]==T->data[j]&&j<T->size){//当模式串与源字符串相等时,都往后比较
            i++;
            j++;
        }
        else if(j>=T->size){//当模式串比较完后
            printf("找到了,下标是:%d",i-j+1);
            return ;
        }
        else if(S->data[i]!=T->data[j]){//当模式串与源字符串不相等时
            if(j==0)
                i++;
            else
                j=next[j-1];
        }
    }
    if(j>T->szie&&i>=S->size)
        printf("找到了,下标是:%d",i-j+1);
    else{
        printf("没有找到");
}
int main(){
    ST S,T;
    int next[100]={0};
    InitString(&S);
    InitString(&T);
    InsertString(&S,&T);
    printf("源字符串是:");
    PrintString(&S);
    printf("\n模式串是:");
    PrintString(&T);
    GetNext(&T,next);
    KMP(&S,&T,next);
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

妮妮妮妮没事吧

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值