串与队列的应用(C语言)

串与队列的应用(C语言)

用KMP算法查找子串在主串中出现的位置与次数,用队列来储存出现的位置与次数并将它们储存在文件中

个人的想法:

a.KMP算法用的next数组而不是nextval数组, 串的第一个位置储存串的长度,从第二个位置开始储存字符串
b.统计其出现的次数:在KMP函数中,如果第一次成功查找到相应的字符串,即子串j = strlen(arr1)(arr1为子串)时,record++并令 j = 1(即回到第一个子串的位置) ,继续对其主串进行遍历,当i > strlen(arr2)(arr2为主串)时,即完成对主串的遍历,此时record的值即为出现的次数
int KMP(char *arr1, char *arr2, int pos, int *next)//用KMP算法寻找子串在主串中出现的次数,pos为要开始寻找的位置
{
    int record = 0;
    int i = pos;
    int j = 1;
    while(i <= arr2[0] && j <= arr1[0])
    {
        if(j == 0 || arr1[j] == arr2[i])
        {
            i++;
            j++;
        }
        else
        j = next[j];
        if(j > arr1[0]) //若已经遍历完子串,则令j=1重新遍历,并用record++记录出现的次数
        {
            j = 1;   //将字串的位置回溯到第一个继续进行遍历
            record++;//成功遍历就加一
        }
    }
    printf("该字符串一共出现了%d次\n",record);
    return record;
}
c.统计其出现的位置:KMP1函数返回的是第一个出现相同字符串的位置,若不出现则不进入main函数中的循环,若出现的进入循环,且出现的位置都用pos数组记录,其中其第一个位置记录的是第一次出现相同字符串的位置,通过其判断是否主串中出现相同的字符串,也是判断是否进入循环的依据。经过循环后,其出现的位置均在pos数组中。
int KMP1(char *arr1, char *arr2, int pos, int *next)//用KMP算法寻找子串在主串中出现的位置
{
    int record = 0;
    int i = pos;//i对应主串
    int j = 1;//j对应子串
    while(i <= arr2[0] && j <= arr1[0])
    {
        if(j == 0 || arr1[j] == arr2[i])
        {
            i++;
            j++;
        }
        else
        j = next[j];       
    }

    if(j > arr1[0]) 
    return (i-arr1[0]);
    else
    return -1;
}
//***以下main函数中的部分代码***
case 5:  //寻找
            record = KMP(T1.arr, T2.arr, 1, next);
            pos[0] = KMP1(T1.arr, T2.arr, 1, next);
            if(pos[0] == -1)
            {
                printf("主串中无对应的字串\n");
            }
            else
            {
                while(pos[j] != -1 )
                {
                    j++;
                    pos[j] = KMP1(T1.arr, T2.arr, pos[j-1]+strlen(arr1), next);
                }
                printf("该子串在主串中出现的位置为%d\n", record);
                printf("其出现的位置为:");
                for(j = 0 ; j < record ; j++)
                {
                    printf("%d ",pos[j]);
                }
                printf("\n");
            }
            break;
d.队列储存思想简述:将pos数组依次赋值到队列中即可

以下是程序的完整代码:

#include <stdio.h>
#include <string.h>
#define OK 1
#define ERROR -1
#define MAXSIZE 200
char *s_gets(char *st, int n)//构造一个去除换行符的输入方法
{
    char *find;
    char *ret_val;
    ret_val = fgets(st, n, stdin);
    if(ret_val)
    {
        find = strchr(st,'\n');
        if(find)
        *find = '\0';
        else
        {
            while(getchar() != '\n')
            continue;
        }
    }
    return ret_val;
}
typedef struct // 定义串的结构
{
    char arr[MAXSIZE];
}String;
int StrAssign(String *T ,char *arr) // 赋值
{
    int i;
    if(strlen(arr) > MAXSIZE)
    return ERROR;
    else
    {
        T->arr[0] = strlen(arr);
        for(i = 1 ; i <= strlen(arr) ; i++)
        {
            T->arr[i] = *(arr+i-1);
        }
    }
    return OK;
}
void GetNext(char *arr, int* next) // 求next数组
{
    int i = 1; // 从一开始 ,则其最长前后缀的值为各个next数组的值减去一
    int j = 0;
    next[1] = 0;
    while(i < arr[0])
    {
        if(j == 0 || arr[j] == arr[i]) //j等于0是其回溯的结果;
        {
            j++;
            i++;
            next[i] = j;
        }
        else
        j = next[j]; //回溯,寻找其当前最大前后缀的最大前后缀
    }
    for(i = 1; i < strlen(arr) ; i++) // 打印出next数组
    {
        printf("next[%d] = %d\n" ,i ,next[i]);
    }
    printf("已成功给next数组赋值\n");
}
int KMP(char *arr1, char *arr2, int pos, int *next)//用KMP算法寻找子串在主串中出现的次数,pos为要开始寻找的位置
{
    int record = 0;
    int i = pos;
    int j = 1;
    while(i <= arr2[0] && j <= arr1[0])
    {
        if(j == 0 || arr1[j] == arr2[i])
        {
            i++;
            j++;
        }
        else
        j = next[j];
        if(j > arr1[0]) //若已经遍历完子串,则令j=1重新遍历,并用record++记录出现的次数
        {
            j = 1;   //将字串的位置回溯到第一个继续进行遍历
            record++;//成功遍历就加一
        }
    }
    printf("该字符串一共出现了%d次\n",record);
    return record;
}
int KMP1(char *arr1, char *arr2, int pos, int *next)//用KMP算法寻找子串在主串中出现的位置
{
    int record = 0;
    int i = pos;//i对应主串
    int j = 1;//j对应子串
    while(i <= arr2[0] && j <= arr1[0])
    {
        if(j == 0 || arr1[j] == arr2[i])
        {
            i++;
            j++;
        }
        else
        j = next[j];       
    }

    if(j > arr1[0]) 
    return (i-arr1[0]);
    else
    return -1;
}
typedef struct{        // 定义队列
    int head;
    int tail;
    int arr[MAXSIZE];
}Queue;
void InitQueue(Queue *p)
{
    p->head = 0;
    p->tail = 0;
}
void isEmpty(Queue *p)   //判断队列是否为空
{
    if(p->head == p->tail)
    printf("这个队列为空\n");
    else
    printf("这个队列不为空\n");
}
void isFull(Queue *p)   //判断队列是否已满
{
    if(p->head == ((p->tail + 1) % MAXSIZE))
    printf("此队列已满\n");
    else
    printf("此队列未满\n");
}
void GetIn_queue(Queue *p, int num)  //向队列输入值
{
    int i;
    i = p->tail;
    p->arr[i] = num;
    p->tail++;

    if((p->tail + 1) % MAXSIZE == p->head)
    printf("此队列已满\n");
}
void Print_queue(Queue *p)   //打印出队列中的值
{
    int i, j;
    i = p->head;
    j = p->tail;
    printf("其出现的次数为 :");
    printf("%d\n" , p->arr[i]);
    printf("其出现的位置分别是:");
    for(i = p->head+1 ; i < j ; i++)
    {
        printf("%d ", p->arr[i]);
    }
    printf("\n");
}
int main()
{   //列表
    printf("1. 打开文件\n");
    printf("2. 赋值\n");
    printf("3. 建立next数组\n");
    printf("4. 给next数组赋值\n");
    printf("5. 寻找\n");
    printf("6. 存入队列\n");
    printf("7. 打印队列\n");
    printf("8. 输入文件\n");
    printf("9. 关闭文件\n\n");
    printf("输入0退出程序\n");
    //声明变量
    FILE *q;
    FILE *r;
    String T1;
    String T2;
    char arr1[MAXSIZE] = "ezreal";
    char arr2[MAXSIZE];
    int *next;  //创建next数组
    int record;  //记录出现的次数
    int pos[MAXSIZE]; //记录出现的位置
    int j = 0;
    int choose = -1;
    int judge;
    Queue p;

    while(choose != 0)
    {
        printf("请选择:\n");
        scanf("%d",&choose);
        switch(choose)
       {
        case 1:// 建立
            q = fopen("C:\\Ezreal\\Num2\\1.txt", "r");
            r = fopen("C:\\Ezreal\\Num2\\2.txt", "w");
            if(q && r)
            printf("成功打开文件\n");
            else
            printf("打开文件失败\n");
            break;
        case 2: //赋值
            //printf("请输入子字符串:\n");
            //fgets(arr1, MAXSIZE,stdin);//从终端输入子字符串
            fgets(arr2, MAXSIZE, q);//从文件输入主字符串
            StrAssign(&T1, arr1);//将其字符串数组的第一个值改为字符串的长度
            StrAssign(&T2, arr2);
            break;
        case 3:  //建立next数组
            next = (int *)malloc(sizeof(int) * strlen(arr1)); //创建next数组
            if(next != NULL) // 判断是否创建成功
            printf("成功创建next数组\n");
            else
            printf("创建失败\n");
            break;
        case 4:  //给next数组赋值
            GetNext(T1.arr, next);
            break;
        case 5:  //寻找
            record = KMP(T1.arr, T2.arr, 1, next);
            pos[0] = KMP1(T1.arr, T2.arr, 1, next);
            if(pos[0] == -1)
            {
                printf("主串中无对应的字串\n");
            }
            else
            {
                while(pos[j] != -1 )
                {
                    j++;
                    pos[j] = KMP1(T1.arr, T2.arr, pos[j-1]+strlen(arr1), next);
                }
                printf("该子串在主串中出现的位置为%d\n", record);
                printf("其出现的位置为:");
                for(j = 0 ; j < record ; j++)
                {
                    printf("%d ",pos[j]);
                }
                printf("\n");
            }
            break;
        case 6:  //存入队列
            InitQueue(&p);
            isEmpty(&p);
            GetIn_queue(&p, record); // 队列的第一个位置存入出现的次数

            for(j = 0 ; j < record ; j++)  //之后存入出现的位置
            {
                GetIn_queue(&p, pos[j]);
            }

            if(p.head != p.tail)
            printf("存入成功\n");
            else
            printf("存入失败\n");
            break;
        case 7:  //打印队列的内容
            Print_queue(&p);
            isFull(&p);  // 判断是否已满
            break;
        case 8:  //输入到文件
            judge = fprintf(r, "该子串在主串中出现的位置为%d\n", record);
            fprintf(r,"其出现的位置为:");
            for(j = 0 ; j < record ; j++)
            {
                fprintf(r, "%d ", pos[j]);
            }

            if(judge > 1)   //判断是否成功输入文件
            printf("成功输入文件\n");
            else
            printf("输入文件失败\n");
            break;
        case 9:  //关闭文件
            fclose(q);
            fclose(r);
            break;
        default:
            if(choose == 0)
            printf("成功退出\n");
            else
            printf("请重新输入数字,");
        }
    }
}

学艺不精,如有错误,欢迎指出

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值