すのはら荘春原庄的雪

目录

  1. 字符串匹配
    1. KMP的基本思想#
      1. 一般方法的弊端:#
      2. 问题在哪?!#
    2. 数学分析#
    3. next数组详解#
      1. next的文字描述#
      2. next实现:#
    4. KMP算法C语言实现#

字符串匹配的改进算法——KMP算法(C/C++)

Toretto·2022-03-25 17:21·140 次阅读

字符串匹配的改进算法——KMP算法(C/C++)

字符串匹配

关于什么是KMP操作,详情见

点我快速了解什么是KMP算法


KMP的基本思想#

一般方法的弊端:#

下面可以不看~~感兴趣可以看看~~,总之就是说:一般方法在面对重复率极高的串时匹配效率极低~

首先,当主串为 00000000000000000000000000000000000000000000000000001 ,而模式串为 00000001 时,由于主串中前七个字符为 0 ,模式串中前52个字符为 0 ,每一轮比较都在模式串的最后一个字符处失配,此时需要将指针(游标) i (指示主串),回溯到 i-6 的位置上,并从模式串的第一个字符开始重新比较。整个匹配过程 指针(游标) i 需要回溯45次, while 循环需要执行 46 * 8 * (Index * m)。在 worst situation下 时间复杂度为O(m*n)

问题在哪?!#

每次失配之后,模式串偏移量太小,导致重复匹配。如果你看了上面那段废话,就可以理解为,指针每次失配之后都回溯至起始位置,导致效率低下!

解决办法:指针不再回溯,一次遍历主串与模式串匹配即可完成!!

数学分析#

来一段数学分析,不感兴趣的同学下滑再下滑就行~~

正文开始:

指针如何不回溯?只需要将模式串失配后,沿着主串方向滑动一定的长度,使主串部分可以与模式串匹配,不需要回溯指针。

Copy
highlighter-hljs
假设主串为S[i],模式串为P[j],其中i指示主串的位置即为主串指针(浮标),j指示模式串的位置即为模式串指针(浮标)。
在一次匹配之后,设第j个字符匹配失败,假设此时主串在指针(游标)i在不回溯的前提下,应与模式串中的第 k (k<j)个字符继续匹配;则一定会有如下关系式:
P[1]P[2]...P[k-1] = S[i-k+1]S[i-k+2]...S[i-1] ----(1
这次匹配的前一次的匹配结果:
P[j-K+1]P[j-k+2]...P[j-1] = S[i-k+1]S[i-k+2]...S[i-1] ----(2
联立式(1)和(2),消去主串可以得到:
P[1]P[2]...P[k-1] = P[j-K+1]P[j-K+2]...P[j-1]
根据此式子可以得出如下结论:
主串中第i位应与模式串中第 k(k<j) 位对齐,且 k 仅与模式串自身有关,因此,匹配仅需从模式串中第 k 个字符和主串中第 i 个字符比较起继续进行。

如上述所得:此时应求模式串的k值!为了确定k,需要一个next(){next[j]=k}来存储模式串的每一个字符对应的k值~

Copy
highlighter-hljs
define next():
next[j]=k,则next[j]表明当前模式中第j个字符与主串中相应字符“失配”时在模式中需重新和主串中该字符进行比较的字符的位置。
next[j] =
if(j==1) 0;
// Max集合非空
else if(Max{K|1<k<j && p[1]...p[k-1]=p[j-k+1]...p[j-1]})
Max{K|1<k<j && p[1]...p[k-1]=p[j-k+1]...p[j-1]
else 1; //其他情况

next数组详解#

next的文字描述#

废话又来了~不过可以看看~

首先,我们简要分析一下 next()的功能。
假设 i 指示主串,j 指示模式串, k 表示应滑动到的位置。
由我们上面得到的数学公式的结论:k 只与模式串自身有关。

Copy
highlighter-hljs
P[1]P[2]...P[k-1] ”=“P[j-K+1]P[j-K+2]...P[j-1]

那么这个 k 具体又是什么东西呢?
简而言之,就是模式串在第 j 个位置(失配的位置)之前,有 k-1 个字符与模式串从首字符起 k-1 个字符相同,这也是上述数学公式的结论。
那么如何求得模式串的 k 呢?
注意:需要求解的是模式串的第 j-(k-1)j-1 的位置与 1k-1 的位置字符相同
如果使用两层循环求解效率过低,在此处可以使用递归的思想。

即要求 k 这个位置,在每次比较,即模式串与模式串自己匹配(模式串既作为主串,也作为模式串),而我们的next()本身指示的就是模式串应该滑到哪个位置。
所以,应该使用两个指针指示模式串,假设分别为 iji 依次指向模式串的每一个位置, j 指向模式串头地址并听从 next()本身滑动。根据上述定义,模式串的首字符的 next 值应为0i从第 next[1] 开始,jnext[0]开始。

i 指示到模式串中间某个位置时,j 也指示到了模式串中的某个位置(j<i),此时对于next()应有两个选择:一、当前位置下,next[i] == next[j],那么只需要将指针顺移,并且该位置(i指示的位置)的next值就为 j 的值(前面一共判断相同判断了j次)。二、当前位置下,next[i] != next[j],此时,根据next()的定义,j 应该回溯到 next[j] 的位置(这里使用了递归)。

现在从初始状态检查 next[ ]。已知next[1] = 0。初始 j = 0i = 1 ,但next[0]并不存储数据,但是又要为next[1]赋值,避免使用冗长的判断,赋值,则在判断 next[i] 是否等于 next[j] 时,当 j=0 ,直接递增 i,与 jnext[0] 并没有实际意义,next[1] 才有实际意义)。

(这里只是在求模式串的k值,不要与主串,模式串的ij搞混,虽然本质上没有区别)

next实现:#

next()的C语言代码如下:

Copy
highlighter-hljs
// 求模式串T的next函数值并存入数组next。
void get_next(char *T, int **next){
int i=0,j=-1,T_len = strlen(T);
(*next) = (int *)malloc(T_len*sizeof(int));
(*next)[0] = -1;
while(i<T_len-1){
if(j==-1||T[i]==T[j]){
i++;j++;
(*next)[i] = j;
}
else
j = (*next)[j];
}
//for(int i=0;i<strlen(T);++i)
// printf("%d ",(*next)[i]);
//putchar('\n');
}

KMP算法C语言实现#

代码如下:

Copy
highlighter-hljs
// 从主串S的第pos个字符后开始匹配即数组的pos-1个位置
int Index_Kmp(char *S,char *T,int *next,int pos){
int i=pos-1,j=0;
int S_len = strlen(S),T_len = strlen(T);
while(i<S_len && j<T_len){
if(j==-1 || S[i]==T[j]){
++i;
++j;
}
else
j = next[j];
}
if(j>=T_len)
return i-T_len;
else
return -1;
}


作者:Mengjie Ye
出处:https://ymj68520.cnblogs.com/

如果,您希望更容易地发现我的新文章,不妨点击一下绿色通道的关注我

如果您觉得阅读本文对您有帮助,请点击一下右下方的推荐按钮,您的推荐将是我写作的最大动力!
版权声明:本文为博主原创或转载文章,欢迎转载,但转载文章之后必须在文章页面明显位置注明出处,否则保留追究法律责任的权利。如您有任何疑问或者授权方面的协商,请            .
posted @   ymj68520  阅读(140)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
分享到:
目录

打赏

>>

欢迎打赏支持我 ^_^

扫描二维码打赏

了解更多

点击右上角即可分享
微信分享提示