字符串匹配的改进算法——KMP算法(C/C++)
字符串匹配
关于什么是KMP操作,详情见
KMP的基本思想#
一般方法的弊端:#
下面可以不看~~感兴趣可以看看~~,总之就是说:一般方法在面对重复率极高的串时匹配效率极低~
首先,当主串为 00000000000000000000000000000000000000000000000000001
,而模式串为 00000001
时,由于主串中前七个字符为 0
,模式串中前52个字符为 0
,每一轮比较都在模式串的最后一个字符处失配,此时需要将指针(游标) i
(指示主串),回溯到 i-6
的位置上,并从模式串的第一个字符开始重新比较。整个匹配过程 指针(游标) i
需要回溯45次, while
循环需要执行 46 * 8 * (Index * m)
。在 worst situation下 时间复杂度为O(m*n)
。
问题在哪?!#
每次失配之后,模式串偏移量太小,导致重复匹配。如果你看了上面那段废话,就可以理解为,指针每次失配之后都回溯至起始位置,导致效率低下!
解决办法:指针不再回溯,一次遍历主串与模式串匹配即可完成!!
数学分析#
来一段数学分析,不感兴趣的同学下滑再下滑就行~~
正文开始:
指针如何不回溯?只需要将模式串失配后,沿着主串方向滑动一定的长度,使主串部分可以与模式串匹配,不需要回溯指针。
在一次匹配之后,设第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值~
令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
只与模式串自身有关。
那么这个 k
具体又是什么东西呢?
简而言之,就是模式串在第 j
个位置(失配的位置)之前,有 k-1
个字符与模式串从首字符起 k-1
个字符相同,这也是上述数学公式的结论。
那么如何求得模式串的 k
呢?
注意:需要求解的是模式串的第 j-(k-1)
到 j-1
的位置与 1
到 k-1
的位置字符相同。
如果使用两层循环求解效率过低,在此处可以使用递归的思想。
即要求 k
这个位置,在每次比较,即模式串与模式串自己匹配(模式串既作为主串,也作为模式串),而我们的next()
本身指示的就是模式串应该滑到哪个位置。
所以,应该使用两个指针指示模式串,假设分别为 i
,j
。i
依次指向模式串的每一个位置, j
指向模式串头地址并听从 next()
本身滑动。根据上述定义,模式串的首字符的 next
值应为0
。i
从第 next[1]
开始,j
从next[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 = 0
,i = 1
,但next[0]
并不存储数据,但是又要为next[1]
赋值,避免使用冗长的判断,赋值,则在判断 next[i]
是否等于 next[j]
时,当 j=0
,直接递增 i
,与 j
(next[0]
并没有实际意义,next[1]
才有实际意义)。
(这里只是在求模式串的k
值,不要与主串,模式串的i
,j
搞混,虽然本质上没有区别)
next实现:#
next()的C语言代码如下:
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语言实现#
代码如下:
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; }
如果,您希望更容易地发现我的新文章,不妨点击一下绿色通道的【关注我】。
如果您觉得阅读本文对您有帮助,请点击一下右下方的推荐按钮,您的推荐将是我写作的最大动力!版权声明:本文为博主原创或转载文章,欢迎转载,但转载文章之后必须在文章页面明显位置注明出处,否则保留追究法律责任的权利。如您有任何疑问或者授权方面的协商,请 .
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)