KMP算法的一种实现

这次的代码写了好久。。因为有一个问题一直没想透彻。要实现子列相对主列的相对移动,要移动的是子列的指针。。。我一直以为是主列需要一个缩进指针,所以改了好久。。更悲催的是前几次测试阴差阳错的全都对了呃呃呃呃。。后来就发现测试有问题。。。最后才想到。。

另外,在计算前缀值的那一步,我本来写了一个char***类型的数组,是一个char*类型的二维数组,想存子列各个前缀的内容。但是后来发现,第一因为存的是指针,读的时候也是读的最初那块内存(main函数里的内存),所以即使给char**malloc一块空间,存上’\0’,也并不能在读取的时候读取到存进去的截断后的字符串,只能读到最原始的字符串。。。也就是说,我通过复制地址的方式,想存把abcd截断,存成ab’\0’,但是因为读的是最原始的内存地址,一定还是abcd。第二,完全可以通过其他方式实现了这个功能。

本代码在code::blocks17.12中运行正常。

#include <stdio.h>
#include <stdlib.h>

typedef struct prefix_table{
    char** data;//一个char*类型的数组。不复制子字符串的内容,仅把子字符串的指针复制紧螺丝
    int* prefix;//最长公共子列长度的数组
    int size;//子列长度
}table;


//根据目标字符串建立一个前缀表的框架
table* create_table(char* part){
    int len=string_length(part);
    table* newtable=(table*)malloc(sizeof(table));
    newtable->size=len;
    newtable->data=(char**)malloc((len+1)*sizeof(char*));
    newtable->prefix=(int*)malloc((len+1)*sizeof(int));
    for(int i=0;i<=len;i++){
        newtable->data[i]=&part[i];
        newtable->prefix[i]=0;
    }
    newtable->prefix[0]=-1;
    return newtable;
}

//KMP算法本身
int KMP(char* whole,char* part){
    table* t=create_table(part);//根据目标字符串建立一个前缀表的框架
    calculate_prefix(t);//给前缀表填入计算的前缀值
    print_prefix_table(t);//输出前缀表

    int pwhole=0;
    int pt=0;
    int temp;
    int len=string_length(whole);//whole数组长度(计算结束点要用)

    while(t->data[pt][0]!='\0'){
        printf("pwhole=%d\n",pwhole);
        if(whole[pwhole]==t->data[pt][0]){
            printf("主[%d]%c == 子[%d]%c\n",pwhole,whole[pwhole],pt,t->data[pt][0]);
            pwhole++;
            pt++;
        }else{
            printf("主[%d]%c != 子[%d]%c\n---------\n",pwhole,whole[pwhole],pt,t->data[pt][0]);
            temp=t->prefix[pt];
            if(temp==-1){
                if(pwhole>=len){//当起始位置都不足以把part遍历一遍了,就一定不包含了
                    return 0;//不包含
                }
                pwhole++;
                continue;
            }
            pt=temp;
            if(pwhole>=len){//当起始位置都不足以把part遍历一遍了,就一定不包含了
                return 0;//不包含
            }
        }
    }
    return 1;//包含
}


//给前缀表填入计算的前缀值
void calculate_prefix(table* t){
    for(int i=1;i<t->size;i++){
        t->prefix[i]=longest_common_prefix(t->data[0],i);//把填入table中的字符串按不同长度(i)分别计算最长公共子列,并放入table->prefix中
    }
}

//创建一个字符串(s)的子字符串
char* create_substring(char* s,int start,int end){
    int len=string_length(s);
    if(start>s||end>s){
        return -1;
    }
    char* res=malloc((end-start+1)*sizeof(char));
    int res_i=0;
    for(int i=start;i<=end;i++){
        res[res_i++]=s[i];
    }
    res[res_i]='\0';
    return res;
}

//求一个字符串(s)在不同输入长度(len)下的的最长公共子列
int longest_common_prefix(char* s,int len){
    int sublen=len;
    while(--sublen){//子列长度 从大到小进行检验
        if(same_string(create_substring(s,0,sublen-1),create_substring(s,len-sublen,len-1))==1){//看左右子列是否相同
            return sublen;//如果相同就输出子列长度
        }
    }
    return 0;//否则输出0
}

//输出前缀表
void print_prefix_table(table* t){
    for(int i=0;i<t->size;i++){
        printf("[%d]%c(%d)\n",i,*(t->data[i]),t->prefix[i]);
    }
}

//输出字符串长度
int string_length(char* a){
    int i=0;
    while(a[i]!='\0'){
        i++;//i:a的总字符数
    }
    return i;
}

//检查两个字符串是否相等,相等输出1
int same_string(char* a,char* b){
    int i=0;
    while(a[i]==b[i]){
        i++;
        if(a[i]=='\0'&&b[i]=='\0'){
            return 1;
        }
    }
    return 0;
}

int main()
{
    char* a="aaaaaaaaaaaaaaaaaab";
    char* b="aaab";

    printf("%d",KMP(a,b));
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值