算法篇——串的模式匹配算法

一、引入


子串的定位运算通常称为串的模式匹配或者串匹配。此运算相当广泛,比如在搜索引擎、拼写检查、语言翻译、数字压缩等应用中都需要用到串匹配。

常用的串匹配算法有简单的模式匹配算法(BF)和KMP算法。

二、简单的模式匹配算法(BF)


简单的模式匹配算法可以理解为比对算法,是指有一个串S和串T,如果我们需要判断串T是不是串S的子串我们就需要用串T和串S一一比对。具体实现流程如下图表示:

由上图我们可以发现,我们分别用两个指针i、j分别指示S、T中需要比较多的字符位置i的初值为pos,j的初值为1。然后我们发现,如果两个串均未比较到串尾,即i、j均分别小于等于S和T的长度时,则循环执行下面操作:

  • S.ch[i]和T.ch[j]比较,若相等,则i和j分别指示串中的下一个位置,继续比较后续字符;
  • 如果不等,指针后退重新开始匹配,从主串的下一个字符(i=i-j+2)起在再重新和模式的第一个字符开始比较。

如此重复,如果j>T.length,说明模式T中的每个字符依次和主串S中的一个连续字符字符序列相等,那么就匹配成功了,返回和T中的第一个字符相等的字符在S中的序号(i-T.length);反之返回0;

代码描述:

cpp 复制代码
int Index_BF(SString S,SString T,int pos){    //返回T在S中第pos个字符开始第一次出现的位置,若不存在,返回0;
//其中T非空,1<=pos<=S.length
    int i=pos,j=1;
    while(i<=S.length&&j<=T.length){
        if(S.ch[i]==T.ch[j]){
            ++i;
            ++j;
        }                    //两个串需要比较到串尾
        else{
            i=i-j+2;
            j=1;
        }            指针后退重新开始匹配
        if(j>T.length){
            return i-T.length
        }
        else{
            return 0
        }
}

BF算法的思路直观简单,但是当匹配失败时指针总是回溯。这就导致其时间复杂度较高。下面介绍另一种改进算法------KMP算法。

三、KMP算法


先在此处简单介绍下KMP算法。KMP算法与BF的算法不同,在BF算法中当遇到不匹配的值时就会指针回溯。所以当出现最后一个字符不匹配时,首字符就将回溯很多次。这样是很没用必要的。这时候就需要使用KMP算法了。使用它时指针是不回溯的。而他具体是怎么运行的呢?请看图示

这时候我们就将用到以下几个概念了:前缀后缀部分匹配值(PM)。

**前缀:**除去最后一个字符以外的所有头部子串组合,例如"abab"的前缀为{a,ab,aba}

**后缀:**除去的第一个字符以外的所有尾部字符组合,例如"abab"的后缀为{b,ab,bab}

**部分匹配值:**字符串的前后缀元素的交集,例如"abab"的部分匹配值为{ab}。PM=1

这样,我们就得到了一个关于字符串的PM表,如下:

回顾上面的KPM算法的模拟图我们发现子串需要向后移动的位数满足下面这样一个公式:

移动位数=已匹配的字符数−对应最大公共前后缀长度

通过仔细阅读发现使用部分匹配值时,每当匹配失败,就去找它前一个元素的部分匹配值,但是用代码表述起来就显得不太方便。所以,当我们将整个PM表的PM整体右移再+1就能得到一个新的PM数组------next[j]。

若令next[j]=k,则next[j]表明当模式中第j个字符与主串中相应字符"失配"时,在模式中需重新和主串中该字符进行比较的字符的位置。由此课引出模式串的next函数定义:

由此定义可以推断出模式串"abaabcac"的next函数值如下图所示:

综上,我们需要先求取next的函数值才能灵活运用KMP算法来进行串的模式匹配。故在使用KMP算法的第一步就是需要先计算next的函数值。

代码描述:

cpp 复制代码
void get_next(SString T,int next[]){
    //求模式串T中的next函数值并存入数组netx
    int i=1,j=0;
    next[1]=0;
    while(i<T.length){
        if(j==0||T.ch[i]==T.ch[j]){
            ++i;
            ++j;
            next[i]=j;}
        else{
            j=next[i]}
        }
}

当我们完成了next的计算之后,紧接着我们就可以来使用KMP算法了。

代码描述:

cpp 复制代码
int Index_KMP(SString S,SStringT,int pos){
    //利用T的next函数求T在主串S中第pos个字符之后的位置
    //
    int i=pos,j=1;
    while(i<S.length&&j<T.length){    //两个串均为比较到串尾
        if(j==0||S.ch[i]==T.ch[j]){    //继续比较后续字符
            ++i;
            ++j;
        }
        else{
            j=next[j];    //模式串向右移动
        }
        if(j>T.length){
            return i-T.length;    //匹配成功
        }
        else{
            return 0    //匹配失败
        }
}

至此。关于串的模式匹配算法的基本学习我们就已经完成的差不多了。 如果我的内容对你有帮助,在下就厚着脸皮讨个点赞关注。如果你有更好的想法,还望留在评论区让我来参考学习。我将不胜感激并努力创作出更好的内容。

相关推荐
衡玖9 分钟前
c语言闯算法--常用技巧
c语言·数据结构·算法
Lill_bin17 分钟前
Java中的分布式锁:原理、实现与最佳实践
java·开发语言·数据结构·分布式·算法·排序算法·maven
迷路爸爸18027 分钟前
从零构建逻辑回归: sklearn 与自定义实现对比
算法·逻辑回归·sklearn
*TQK*1 小时前
双指针算法介绍+算法练习(2025)
c++·学习·算法·双指针
陈果然DeepVersion1 小时前
算法奈我何(二)冒泡排序
算法
郭涤生1 小时前
并行算法_第十章_《C++并发编程实战》笔记
c++·算法·并发编程
SomeB1oody1 小时前
【Python机器学习】2.2. 聚类分析算法理论:K均值聚类(KMeans Analysis)、KNN(K近邻分类)、均值漂移聚类(MeanShift)
python·算法·机器学习·聚类·分类算法
Cindy辛蒂2 小时前
C语言:计算并输出三个整数的最大值 并对三个数排序
c语言·数据结构·算法
AI技术控2 小时前
计算机视觉算法实战——驾驶员玩手机检测(主页有源码)
算法·计算机视觉·智能手机
小竹子142 小时前
L2-4 吉利矩阵
算法·矩阵·深度优先