串的运算实现(求子串、朴素的模式匹配、KMP算法等)

一、参考知识

  1. malloc()函数:动态分配内存空间

二、代码部分

1. 头文件:string_pattern_matching.h

// 
// Project: String_Pattern_Matching
// File: string_pattern_matching.h
// IDE: CLion
// Created by RichardLau_Cx on 2021/5/20.
//

//#ifndef STRING_PATTERN_MATCHING_STRING_PATTERN_MATCHING_H
//#define STRING_PATTERN_MATCHING_STRING_PATTERN_MATCHING_H
//
//#endif //STRING_PATTERN_MATCHING_STRING_PATTERN_MATCHING_H

/*************************************************************
    串操作的实现  头文件
**************************************************************/


#define MAXSTRLEN 255  // 定义串最大长度
typedef char SString[MAXSTRLEN + 1];  // 定义串,并且将SString替换为char数组类型,0号单元存放串的长度, 用'\0'作结束符


// 求子串
void SubStr(SString t, SString s, int i, int len);

// 以字符数组存储字符串,实现朴素的模式匹配算法
int Index(char S[], char T[], int pos);

// n模式串的next函数
void Get_next(char *p, int next[]);

// KMP模式匹配算法,设模式串第一个字符的下标为0
int Index_KMP(char *s, char *p, int pos, int next[]);

2. 主函数文件:main.cpp

//
// Project: String_Pattern_Matching
// File: functional_operation.cpp
// IDE: CLion
// Code style: AllMan Style - BSD
// Created by RichardLau_Cx on 2022/5/20.
//

#include <iostream>
#include <cstring>
#include "string_pattern_matching.h"

int main() {
    SString s, t;
    int i, len;
    int goOn=1;  // 是否继续使用
    int opt;  // 功能选项
    int pos = 0;  // 索引位置

    while (goOn) {
        printf("\n欢迎来到串的世界,请选择你想要的操作:\n"
               "1. 求子串\n"
               "2. 朴素的模式匹配算法\n"
               "3. KMP模式匹配算法\n\n");

        scanf("%d", &opt);

        switch (opt) {
            case 1:
                printf("\n输入主串:\n");
                scanf("%s", s + 1);
                s[0] = strlen(s + 1);  // 获取输入串的长度(int -> char)
                //    itoa(strlen(s+1), s, 10);  // 获取输入串的长度 -得修进,目前不可行

                printf("\n截取起点(索引):\n");
                scanf("%d", &i);
                printf("\n截取长度:\n");
                scanf("%d", &len);
                SubStr(t, s, i, len);
//                printf("主串长度为: %d\n", s[0]);
//                printf("子串长度为: %d", t[0]);
                break;

            case 2:
                printf("\n输入主串:\n");
                scanf("%s", s);
                printf("\n输入模式串:\n");
                scanf("%s", t);
                pos = Index(s, t, pos);  // 返回模式串在主串中从pos开始的位置(下标)

                if (pos == -1)
                {
                    printf("\n匹配失败\n");
                }

                else
                {
                    printf("\n模式串在主串中开始的位置pos (下标)为:[%d]\n", pos);
                }

                break;

            case 3:
                int i;
                int next[MAXSTRLEN+1];

                printf("\n输入主串:\n");
                scanf("%s", s);  // 主串
                printf("\n输入模式串:\n");
                scanf("%s", t);

                pos = Index_KMP(s, t, 1, next);

                if (pos == -1)
                {
                    printf("\n匹配失败\n");
                }

                else
                {
                    printf("\n模式串在主串中开始的位置pos (下标)为:[%d]\n", pos);
                }

                break;
        }


        printf("\n还要继续使用吗(是为1,否为0)?\n");
        scanf("%d", &goOn);
    }

    return 0;
}

3. 功能操作文件:functional_operation.cpp

// 
// Project: String_Pattern_Matching
// File: functional_operation.cpp
// IDE: CLion
// Code style: AllMan Style - BSD
// Created by RichardLau_Cx on 2022/5/20.
//


#include <cstring>
#include <cstdio>
#include "string_pattern_matching.h"

void SubStr(SString t, SString s, int i, int len)
{
   /** 从s的第i个字符开始,截取长度为len的子串存入t中
    * 其中: 1 <= i <= sLen, 0 <= len <= sLen - i + 1
    * 若i和len超出了取值范围,则输出“error”;否则输出字串t
    */

   int k = 1;
   int j = i;

   if (i < 1 || i > s[0] || len < 0 || len > s[0] - i + 1)
   {
       /**
        * 1. [0]号位置存放着串的长度
        * 2. 截取开始字符位置超过串长
        * 3. 截取长度为0
        * 4. 截取长度大于从i位置开始的剩余串长度
        */

       printf("error");
       return;
   }

    for (k = 1, j = i; k <= len; j++, k++)
    {
        t[k] = s[j];  // 从主串中对应位置i开始,取len个元素进字串
    }

    t[0] = len;  // 截取长度,即子串长度
    t[k] = '\0';  // 串尾存储一个结束符'\0'

    printf("\n所截取的子串为:%s\n", t+1);
//    puts(t+1);  // 相当于从索引为1的地址开始输出子串
}


int Index(char S[], char T[], int pos)
{  /** 查找并返回模式串T在主串S中,从pos开始的位置(下标),若T不是S的子串,则返回-1
    * 模式串T和主串S第一个字符的下标为0
    */

    int i = pos;
    int j = 0;  // i,j分别用于指出主串字符和模式串字符的位置
    int sLen, tLen;  // 主串和模式串的长度

    sLen = strlen(S);
    tLen = strlen(T);

//    printf("i: %d\n", i);
//    printf("sLen: %d\n", sLen);
//    printf("tLen: %d\n", tLen);

    while (i < sLen && j <tLen)
    {  // 都没有匹配至尾部时
        if (S[i] == T[j])
        {
            // 继续同步向后匹配
            i++;
            j++;
        }

        else
        {
            i = i - j + 1;  // 主串字符的位置指针回退,并且主串匹配前进一个位置
            j = 0;  // 子串从头开始
        }
    }

    if (j >= tLen)
    {  // 一直匹配到字串尾 => 假如主串中有多个模式串,则匹配出的是最后边模式串的位置
        return i-tLen;  // 使主串回退到匹配的起点
    }

    return -1;  // 匹配失败
}


void Get_next(char *p, int next[])
{
    /**
     * 求模式串p的next函数值,并存入数组next
     */

    int i, j, pLen;

    pLen = strlen(p);
    i = 0;
    next[0] = -1;  // 若数组没有分配空间,则无法赋值
    j = -1;

    while (i < pLen)
    {
        if (j == -1 || p[i] == p[j])
        {
            ++i;  // i是一直都在往后走,和next部分匹配值表同步
            ++j;
            next[i] = j;  // 相当于存入有多少个相等的前缀后缀
        }

        else
        {
            j = next[j];  // 相当于把之前存入有多少个相等的前缀后缀存至j
        }

        printf("i: %d, j: %d\n",i , j);
    }

    /*  next数组测试
        printf("next: ");
        for (int k = 0; k < pLen; ++k)
        {
            printf("%d ", next[k]);
        }
        printf("\n");
    */
}


int Index_KMP(char *s, char *p, int pos, int next[])
{
    /**
     * 利用模式串p的next函数,求p在主串中从第pos个字符开始的位置
     * 若匹配成功,返回模式串在主串中的位置(下标),否则返回-1
     */

    int i, j, sLen, pLen;

    i = pos-1;
    j = -1;

    sLen = strlen(s);
    pLen = strlen(p);

    Get_next(p, next);  // 对于数组来说,变量名即为数组首地址 - KMP算法的核心

    while (i < sLen && j < pLen)
    {
        if (j == -1 || s[i] == p[j])
        {
            ++i;
            ++j;
        }

        else
        {
            j = next[j];  // j和前面有几个相等前后缀(连续的),就到其对应的下一个位置去继续匹配
        }
    }

    if (j >= pLen)
    {
        return i - pLen;
    }

    else
    {
        return -1;
    }
}

三、配套资源

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值