c语言 BF算法&KMP算法&BM算法

实验:

字符串的匹配算法 
1 测试串的生成 
 (1)随机生成 10,000 个 0~9 十个数字字符构成的文本串 s0。 
(2)模式串 p 定义为 2269162268。 
(2.1)令 i=0 
(2.2)将模式串 p 插入 si的一个随机位置之后,生成新的串 si+1。 
(2.3) i = i+1 
(2.4)转到(2.2),直到 i=10 
(2.5)记最后得到的串为 s* 
(3) 将你的学号分别插入文本串 s*的开始位置和结束位置,使得新的文本串 s 以你的学
号开始和结束。 
(4) 以文件形式输出文本串 s。 
 
2 字符串的匹配实现 
读取以文件形式存储的串,分别应用蛮力算法(BF)、KMP 算法和 BM 算法,找出模式串
p 在文本串 s 中的所有出现位置,并分别输出对应的比较次数。 
 

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
typedef struct str{
    int textstr[10];
}str;      //以十个随机数为一组的文本串
typedef struct string{
    str s[10100];
    int length;
}string;    //题目中数据串*s

void input(string *L)
{//生成s0
    int a;
    L->length=0;      //初始化为0
    srand(time(NULL));
    for(int i=1;i<=10000;i++)      //s[0]留出存学号
    {
        for(int k=0;k<10;k++)
            L->s[i].textstr[k]=rand()%(10);      //int num=rand()%(n-m+1)+m  让随机数在[m,n]范围
        L->length++;
    }
}

void print(string *L)
{//在终端打印
    int i;
    for(i=0;i<=L->length;i++)
    {
        printf("s[%d]:",i);
        for(int k=0;k<10;k++)
            printf("%d",L->s[i].textstr[k]);
        printf("\n");
    }
    printf("length:%d\n",L->length);
}

void insert(string *L,int *p,int *q)
{//插入模式串p和学号
    int a,k;
    // printf("the locationg p insert:");
    for(int i=0;i<10;i++,L->length++)     //p插入重复十次
    {
        a=rand()%(10000)+1;        //从1~10000中随机选一个位置插入
        // printf("%d ",a);
        for(k=L->length;k>=a;k--)
        {//a后的元素全部后移
            for(int j=0;j<10;j++)
            L->s[k+1].textstr[j]=L->s[k].textstr[j];            
        }
        for(int j=0;j<10;j++)      //p插入
            L->s[a].textstr[j]=p[j];
    }
    for(int i=0;i<10;i++)
    {//插入学号到首位与末位
        L->s[0].textstr[i]=q[i];
        L->s[L->length+1].textstr[i]=q[i];
    }
    L->length++;
}

void filewrite(string *L)
{//生成文件并输出串s
    FILE *fp;
    if((fp=fopen("fout.txt","a"))==NULL)	//打开输出文件并使fp指向此文件
	{//a 操作对文件内容追加
		printf("cannot open file\n");
		exit(0);		//若不成功,终止程序
	}
    for(int i=0;i<=L->length;i++)
    {
        for(int j=0;j<10;j++)        //每一行为一个s[]
            fprintf(fp,"%d",L->s[i].textstr[j]);       //文件内容写入
    }
    fclose(fp);
}

char *fileread_translate()
{//从文件中读取文本,从而对复杂结构体进行转化,同时将整型转化为字符型
    FILE *fp;
    char s[10012];
    if((fp=fopen("fout.txt","r"))==NULL)	//打开输出文件并使fp指向此文件
	{
		printf("cannot open file\n");
		exit(0);				//若不成功,终止程序
	}
    char buf[1000111] = { 0 };
    char *p;      //p用于返回buf数组的内容
    while (!feof(fp)) //没有到文件末尾
    {
        memset(buf, 0, sizeof(buf));    //初始化数组为0
        fread(buf, sizeof(char), sizeof(buf), fp);
    }
    fclose(fp);
    strcpy(p,buf);
    return p;         //通过指针返回一个数组
}

/*BF算法*/
int BF(char *s,char *t)
{//BF算法 s是原字符串,t是匹配字符串
    int m,n;
    int i=0,j=0,k=0;         //从0位置开始匹配
    m= strlen(s);
    n = strlen(t);
    // printf("%d %d",m,n);
    while(i<=m)
    {
    while (i<=m&&j<n)       //m,n是串长
    {
        k++;
        if (s[i]==t[j])
        {
            i++;
            j++;        //逐个匹配,成功s++   t++
            // printf("%d %d\n",i-1,j);
        }
        else
        {
            i=i-j+1;       //不成功,s返回到此次循环匹配的初始位置
            j=0;           //不成功,t返回到0位置
            // printf("%d %d\n",i,j);

        }
    }
    if(j>n-1)
    {
        printf("\nlocation:%d",i-n);
        i=i-j+1;       //不成功,s返回到此次循环匹配的初始位置
        j=0;           //不成功,t返回到0位置
    }
    }
    return k;          //返回比较次数
}

/*KMP算法*/
void get_next(char *s,int *next)
{//生成next表
	int len=0;
    int i=0;
    int j=-1;
    next[0]=-1;       //首位为-1
    len=strlen(s);
    while(i<len-1)
    {//当不超过模式串长度时
        if(j==-1||s[i]==s[j])
        {//若有数与前面的相等或为首位,next对应数值加一
            i++;
            j++;
			next[i]=j;
        }
        else
        {
            j=next[j];
        }
    }
    // printf("\n%d",next);
}
int KMP(char *s1,char *s2,int *next)
{//KMP主算法
    int i=0;
    int j=0;
    int k=0;
    int len1=strlen(s1);
    int len2=strlen(s2);
    while (i<len1){
    while(i<len1&&j<len2)
    {//当不超过数组长度时,一直执行
        k++;//计数
        if(j==-1||s1[i]==s2[j])
        {//若匹配到了,往后移一位并重复比较
            i++;
            j++;
        }
        else
        {
            j=next[j];
        }
        // printf("%d %d\n",i,j);
    }
    if(j>=len2)
    printf("location:%d\n",i-len2);
    i++;
    j=0;
    }
    return k;
}

/*BM算法*/
/*  
    函数:int* MakeSkip(char *, int)  
    目的:根据坏字符规则做预处理,建立一张坏字符表  
    参数:  
        ptrn => 模式串P  
        PLen => 模式串P长度  
    返回:  
        int* - 坏字符表  
*/   
int * MakeSkip( char  *ptrn)
{//建立坏字符表,申请256个int的空间
    int  i;
    int pLen=strlen(ptrn);
    int  *skip = ( int *)malloc(256* sizeof ( int ));
    if (skip == NULL)
    {
        fprintf(stderr, "malloc failed!" );
        return  0;
    }     
    //初始化坏字符表,256个单元全部初始化为pLen   
    for (i = 0; i < 256; i++)
    {
        *(skip+i) = pLen;
    }
  
    //给表中需要赋值的单元赋值,不在模式串中出现的字符就不用再赋值了 
    while (pLen != 0)
    {
        *(skip+(unsigned char )*ptrn++) = pLen--;
    }
    return  skip;
}  
  
  
/*  
    函数:int* MakeShift(char *, int)  
    目的:根据好后缀规则做预处理,建立一张好后缀表  
    参数:  
        ptrn => 模式串P  
        PLen => 模式串P长度  
    返回:  
        int* - 好后缀表  
*/   
int * MakeShift( char * ptrn)  
{//为好后缀表申请pLen个int的空间
    int pLen=strlen(ptrn);
    int  *shift = ( int *)malloc(pLen* sizeof ( int ));  
    int  *sptr = shift + pLen - 1; //方便给好后缀表进行赋值的指标   
    char  *pptr = ptrn + pLen - 1; //记录好后缀表边界位置的指标   
    char  c;  
  
    if (shift == NULL)  
    {  
        fprintf(stderr,"malloc failed!" );  
        return  0;  
    }  
    c = *(ptrn + pLen - 1);     //保存模式串中最后一个字符,因为要反复用到它   
    *sptr = 1;     //以最后一个字符为边界时,确定移动1的距离   
    pptr--;        //边界移动到倒数第二个字符(这句是我自己加上去的,因为我总觉得不加上去会有BUG,大家试试“abcdd”的情况,即末尾两位重复的情况)   
    while (sptr-- != shift) //该最外层循环完成给好后缀表中每一个单元进行赋值的工作   
    {
        char  *p1 = ptrn + pLen - 2, *p2,*p3;  
        //该do...while循环完成以当前pptr所指的字符为边界时,要移动的距离   
        do {  
            while (p1 >= ptrn && *p1-- != c);    //该空循环,寻找与最后一个字符c匹配的字符所指向的位置   
            p2 = ptrn + pLen - 2;  
            p3 = p1;  
            while (p3 >= ptrn && *p3-- == *p2-- && p2 >= pptr);        //该空循环,判断在边界内字符匹配到了什么位置   
        }while (p3 >= ptrn && p2 >= pptr);  
        *sptr = shift + pLen - sptr + p2 - p3;        //保存好后缀表中,以pptr所在字符为边界时,要移动的位置   
        /*  
          PS:*sptr = (shift + pLen - sptr) + p2 - p3;  
            括号括起来的部分,如果只需要计算字符串移动的距离,那么括号中的那部分是不需要的。  
            因为在字符串自左向右做匹配的时候,指标是一直向左移的,这里*sptr保存的内容,实际是指标要移动  
            距离,而不是字符串移动的距离。
        */   
        pptr--;    //边界继续向前移动   
    }  
    return  shift;  
}  
  
/*  
    函数:int* BMSearch(char *, int , char *, int, int *, int *)  
    目的:判断文本串T中是否包含模式串P  
    参数:  
        buf => 文本串T  
        blen => 文本串T长度  
        ptrn => 模式串P  
        PLen => 模式串P长度  
        skip => 坏字符表  
        shift => 好后缀表  
    返回:  
        int - 1表示成功(文本串包含模式串),0表示失败(文本串不包含模式串)。  
*/   
int  BMSearch( char *buf,char *ptrn,int *skip,int *shift)  
{
    int i=0,j;     //记录比较次数
    int blen=strlen(buf);
    int plen=strlen(ptrn);
    int  b_idx = plen;      //b_idx赋值为模式串长度
    while (b_idx <= blen)
    {//计算字符串是否匹配到了尽头
    int  p_idx = plen, skip_stride, shift_stride;
        i++;
        while  (buf[--b_idx] == ptrn[--p_idx])
        {//开始匹配 
            i++;
            if (p_idx == 0)
            {
                j=1;      //标志已经查找到一处
                if(b_idx>=0)
                printf("location:%d\n",b_idx);
            }  
        }
        /*跳转*/
        if(j==0)     //若没有查找到
        {
            skip_stride = skip[(unsigned char )buf[b_idx]];    //根据坏字符规则计算跳跃的距离   
            shift_stride = shift[p_idx];    //根据好后缀规则计算跳跃的距离   
            b_idx += (skip_stride > shift_stride) ? skip_stride : shift_stride;     //取大者
        }
        if(j==1)    //若已经查找到,则b_idx移动到下一位,p_idx重置为10
        {       
            b_idx+=plen*2;
            p_idx=plen;
            j=0;
        }
    }
    return i;
}  

int main()
{
    int num[]={2,1,1,5,3,0,0,0,0,8};
    int p[]={2,2,6,9,1,6,2,2,6,8};
    string L;
    input(&L);          //生成s
    insert(&L,p,num);   //插入p与学号
    // filewrite(&L);      //以文件形式输出
    char *buf=fileread_translate();  //读取文件
    // printf("%s",buf);      //buf为长度为10011*10的文本串,以下算法由它进行运算
    printf("\n");
    char *t="2269162268";
    char *q="2115300008";

    /*BF算法*/
    printf("BF start:");
    int k;
    k=BF(buf,t);
    printf("\n%d\n",k);

    /*KMP算法*/
    printf("\nKMP start:\n");
    int next[11];
    get_next(t,next);
    k=KMP(buf,t,next);
    printf("%d\n",k);

    /*BM算法*/
    printf("\nBM start:\n");
    int *bad=MakeSkip(t);
    int *good=MakeShift(t);
    int BM=BMSearch(buf,t,bad,good);
    printf("%d",BM);
}

代码计算的比较次数有问题,我一个小菜鸟不知道怎么改,有大佬懂的可以联系我!

BF算法KMP算法和BM算法都参考了其他大神的代码,我只是对代码进行包装以符合实验要求,因找的代码很多现在难以找到原始的参考代码了,在此抱歉,侵删。

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值