C 语言| 字符串匹配BF算法与RK算法

简介: 字符串匹配算法最经典的手段是BF算法,字符串匹配即给出一个主串S,根据模式串T中的字符串,找出在主串中第一次出现的位置,这个就是字符串匹配,简而言之即给一个规定的内容T,在大范围S中找到一个与之对应的,且第一次出现的位置。

🌟前言


字符串匹配算法最经典的手段是BF算法,字符串匹配即给出一个主串S,根据模式串T中的字符串,找出在主串中第一次出现的位置,这个就是字符串匹配,简而言之即给一个规定的内容T,在大范围S中找到一个与之对应的,且第一次出现的位置。

网络异常,图片无法展示
|

BF算法


BF算法全称叫BruteForce,如本名一样,BF算法十分简单暴力,对主串和模式串进行逐个字符比较,中文名叫暴力匹配算法或者朴素匹配算法

而BF算法就是完成对模式串的移动,一位一位地往后移,然后从第一个字母开始比较,直到找到三个字母都相等的位置。

代码实现


首先我们先来写几个基本函数

/*生成一个其值等于chars的串T*/
Status StrAssign(String T,char *chars)
{
    int i;
    if(strlen(chars)>MAXSIZE)
        return ERROR;
    else{
        T[0]=strlen(chars);     //T[0]存字符串的长度
        for(i=1;i<=T[0];i++)
            T[i]=*(chars+i-1);
        return OK;
        }
}
/*清除字符串,直接令其长度为0*/
Status ClearString(String S)
{
    S[0]=0;
    return OK;
}
/*打印字符串*/
void StrPrint(String T)
{
    int i;
    for(i=1;i<T[0];i++)
        printf("%c",T[i]);
    printf("\n");
}

下面才是我们真正要开始写的BF算法的代码

先传进一个主串,模式串,然后再来一个pos(pos数值为多少,就代表从主串的第几个字母开始查找)

int Index_BF(String S,String T,int pos){
}

接着呢我们就写while循环,一步一步遍历我们的主串和模式串,分别用i,j变量。这里要注意的是,为了方便,我们默认把S[0]和T[0]存入他们各自的串长,也就是遍历从i=1和j=1开始,这样子也更方便我们书写代码,明确代码遍历到了哪一步

然后在while循环里添加if循环,判断S[i]和T[j]是否相等,相等就移到下一位,继续判断,移动就用i++和j++即可

如果不相等,证明遇到了不相同的字母,就让它俩回溯,让i回到初始位置,然后后移,而j回到1,又从模式串第一位开始判断,详细回溯解析看此代码后面的图解

int Index_BF(String S,String T,int pos){
    //主串索引
    int i = pos;
    //模式串索引
    int j = 1;
    while (i<= S[0] && j<=T[0]){
        //S[0]和T[0]的数值代表他们的长度
        if(S[i] == T[j]){
            i++;
            j++;
        }else{
            //回溯,索引下标更改
            i = i-j+2;
            j = 1;
        }
    }
    if(j>T[0]){
        return i-T[0];
    }
    return -1;
}

if循环实现回溯图解

网络异常,图片无法展示
|

以此时i=3为例,正好判断到了第三位,S[i]和T[j]不相等,此时我们发现i = i -j +1是回到原位

网络异常,图片无法展示
|

那我们让其再+1,即i = i - j +2,就可以使其回到原位后再进一位,接着令j = 1,回到模式串第一位进行S[i-j+2]==T[j]的判断

网络异常,图片无法展示
|

此时i=i-j+2=2,S[2]不等于T[1],又一次执行i = i - j +2,这时的i = 3,j = 1,通过这个循环,一步一步后移,实现了我们的BF算法,真的是简单粗暴,这个代码实现如果不能理解,就多看几遍图解,我相信你会有所收获

网络异常,图片无法展示
|

BF算法的时间复杂度很高,但有一个很好的优点就是:代码实现简单,在实际开发中,简单意味着,不容易出错,满足我们所说的KISS,咳咳,不是那个意思哈,是keep it simple and stupid(doge)

RK算法


RK算法是由两位发明者Rabin和Karp的名字来命名的,这个来历应该很容易理解吧(外国人都喜欢用自己名字命名一个新鲜事物哈哈)

相对于BF算法,RK算法比较的不是字母,而是数字,利用到哈希表这种算法,需要计算出哈希值

拿我们的例题来解释

模式串有三个字母,那我们就把主串分别以三个字母为一组子串,全部分隔出来,转化成我们的哈希值h1,h2,h3······,然后再和模式串比较,与哈希值相同的子串,就是我们要匹配到的,在主串中的位置

网络异常,图片无法展示
|

在这里边我们还可以稍作修改,例如我们没必要把所有子串都转化好,再进行与模式串的比较。为了去掉多余的步骤,我们可以边转化,边比较

假设我们的模式串为dda,此时转完第一个abd,不匹配;接着转第二个bdd,也不匹配;此时转到第三个,哎!是dda了,哈希值和模式串相同,这时我们就节省了后面dab,abc等子串的转化。

此时就有疑问了,我们该怎么把这模式串和拆出来的各个子串换算成哈希值呢?

首先我们先看一下十进制位的数字分解,以三位数为例

网络异常,图片无法展示
|

从上面的步骤看,abc的哈希值就是28,有了这套理论,我们就可以开始实践了

//RK算法
int getIndex_RK(String strOne, String strTwo){
    //1、记录两个字符串的长度
    int lengthOne = strOne[0];
    int lengthTwo = strTwo[0];
    //2、计算模式串的Hash值
    long long twoHashValue = 1;
    for (int i = 1; i <= lengthTwo; i++) {
        int value = strTwo[i] - 'a' + 1;
        twoHashValue *= value;
    }
    //3、依次计算出主串每个子串的Hash值,边计算边比较
    long long oneHashValue = 1;
    for (int i = 1; i <= lengthOne - lengthTwo + 1; i++) {
        if (i == 1) {
            for (int j = 1; j <= lengthTwo; j++) {
                int value = strOne[j] - 'a' + 1;
                oneHashValue *= value;
            }
        }
        else {
            //4、计算新子串的Hash值可以根据旧子串的Hash计算得出,减少重复计算
            int valueOld = (strOne[i - 1] - 'a' + 1);
            int valueNew = (strOne[i + lengthTwo - 1] - 'a' + 1);
            oneHashValue = oneHashValue / valueOld * valueNew;
        }
        //5、Hash值相同需对比一下子串和模式串是否匹配(防止出现哈希冲突),匹配则返回index
        if (oneHashValue == twoHashValue) {
            int isOK = isMatch(strOne, i, strTwo);
            if (isOK == 1) {
                return I;
            }
        }
    }
    //6、如果没有找到匹配的子串,则返回-1
    return -1;
}
//判断Hash值相等的字符串是否相等
int isMatch(String strOne, int index, String strTwo){
    for (int i = 1; i <= strTwo[0]; i++) {
        if (strOne[index + i - 1] != strTwo[i]) {
            return 0;
        }
    }
    return 1;
}
相关文章
|
27天前
|
存储 人工智能 算法
数据结构与算法细节篇之最短路径问题:Dijkstra和Floyd算法详细描述,java语言实现。
这篇文章详细介绍了Dijkstra和Floyd算法,这两种算法分别用于解决单源和多源最短路径问题,并且提供了Java语言的实现代码。
62 3
数据结构与算法细节篇之最短路径问题:Dijkstra和Floyd算法详细描述,java语言实现。
|
3月前
|
存储 算法 C语言
"揭秘C语言中的王者之树——红黑树:一场数据结构与算法的华丽舞蹈,让你的程序效率飙升,直击性能巅峰!"
【8月更文挑战第20天】红黑树是自平衡二叉查找树,通过旋转和重着色保持平衡,确保高效执行插入、删除和查找操作,时间复杂度为O(log n)。本文介绍红黑树的基本属性、存储结构及其C语言实现。红黑树遵循五项基本规则以保持平衡状态。在C语言中,节点包含数据、颜色、父节点和子节点指针。文章提供了一个示例代码框架,用于创建节点、插入节点并执行必要的修复操作以维护红黑树的特性。
99 1
|
13天前
|
存储 算法 数据管理
C语言算法复杂度
【10月更文挑战第20天】
C语言算法复杂度
|
1月前
|
算法
两个字符串匹配出最长公共子序列算法
本文介绍了最长公共子序列(LCS)问题的算法实现,通过动态规划方法求解两个字符串的最长公共子序列,并提供了具体的编程实现细节和示例。
64 1
两个字符串匹配出最长公共子序列算法
|
1月前
|
C语言 C++
【C语言】解决不同场景字符串问题:巧妙运用字符串函数
【C语言】解决不同场景字符串问题:巧妙运用字符串函数
|
29天前
|
存储 算法 C语言
【C语言】二分查找算法
【C语言】二分查找算法
|
2月前
|
存储 C语言
【C语言基础考研向】10 字符数组初始化及传递和scanf 读取字符串
本文介绍了C语言中字符数组的初始化方法及其在函数间传递的注意事项。字符数组初始化有两种方式:逐个字符赋值或整体初始化字符串。实际工作中常用后者,如`char c[10]=&quot;hello&quot;`。示例代码展示了如何初始化及传递字符数组,并解释了为何未正确添加结束符`\0`会导致乱码。此外,还讨论了`scanf`函数读取字符串时忽略空格和回车的特点。
|
1月前
|
算法 安全 Go
Python与Go语言中的哈希算法实现及对比分析
Python与Go语言中的哈希算法实现及对比分析
35 0
|
2月前
|
存储 Serverless C语言
【C语言基础考研向】11 gets函数与puts函数及str系列字符串操作函数
本文介绍了C语言中的`gets`和`puts`函数,`gets`用于从标准输入读取字符串直至换行符,并自动添加字符串结束标志`\0`。`puts`则用于向标准输出打印字符串并自动换行。此外,文章还详细讲解了`str`系列字符串操作函数,包括统计字符串长度的`strlen`、复制字符串的`strcpy`、比较字符串的`strcmp`以及拼接字符串的`strcat`。通过示例代码展示了这些函数的具体应用及注意事项。
|
1月前
|
搜索推荐 C语言 C++
【C语言】指针篇-精通库中的快速排序算法:巧妙掌握技巧(4/5)
【C语言】指针篇-精通库中的快速排序算法:巧妙掌握技巧(4/5)
下一篇
无影云桌面