KMP算法详解

一、 KMP算法解决什么问题?

KMP解决的是用线性复杂度在主串中查找第一次出现模式串的下标。
如果使用普通方法,那就是用二重循环搜索,时间复杂度为 O(M*N)。M为主串长度,N为模式串长度。

【举例子】
使用KMP算法,我们可以用 O(N) 的时间复杂度在主串"abcdef@abga@cd"中查找模式串 “abga”。

二、理解KMP算需要理解哪些部分?
  1. 前缀、后缀概念
  2. n e x t [ N ] next[N] next[N]数组
  3. n e x t [ N ] next[N] next[N]数组的迭代求法
  4. 根据 n e x t [ N ] next[N] next[N]数组移动指向模式串的指针匹配主串
三、 拆分讲解
1. 前后缀概念、next[N]数组的意义

【举例子】
字符串str="babdefbab"的不同长度的前缀、后缀如表1。

长度前缀后缀
1bb
2baab
3babbab
4babdfbab
5babdeefbab
6babdefdefbab
7babdefbbdefbab
8babdefbaabdefbab
9babdefbabbabcdefbab

其中相同长度的前缀和后缀相等的有b、bab、babdefbab。
相同长度的真前缀和真后缀相等的有b、bab。其中长度最长的相等真前后缀为bab。
【概念】
根据上面的例子我们可以抽象前缀、后缀的概念

  • 前缀指字符串的任意首部。 真前缀指不包含本身字符串的任意首部,字符串的真前缀长度要小于字符串的长度。
  • 后缀指字符串任意尾部。字符串的真后缀的长度要小于字符串的长度。
2. n e x t [ N ] next[N] next[N]数组的意义

【概念】
n e x t [ i ] next[i] next[i]表示 s t r str str的下标为 0 0 0 i − 1 i-1 i1 的子串的最长的相等的真前后缀的长度。 n e x t [ i ] next[i] next[i]的范围为[-1,N-1]。

【举例子】 仍然用字符串str="babdefbab"举例

i012345678
next[i]-100100012
3. n e x t [ N ] next[N] next[N]数组的迭代求法
  • 先看代码
void nextSolution(int *next,int len,char *pattern){
    int index=0,k=-1;
    next[0]=k;
    while(index<len-1){
        if(k==-1||pattern[index]==pattern[k]){//注意next[0]=-1的特例
            next[++index]=++k;
        }else{
            k=next[k]; //调整k
        }
    } 
}
  • 看图解释
    初始值 i = 0 , n e x t [ i ] = 0 , k = n e x t [ i ] i=0,next[i]=0,k=next[i] i=0,next[i]=0,k=next[i]
  1. k = = − 1 k==-1 k==1时,next[i+1]=k+1。
    在这里插入图片描述
  2. s t r [ i ] = s t r [ k ] str[i]=str[k] str[i]=str[k]时,next[i+1]=k+1。
    在这里插入图片描述
  3. s t r [ i ] ≠ s t r [ k ] str[i] \ne str[k] str[i]=str[k]时,迭代k=next[k]。
    在这里插入图片描述
4. 根据 n e x t [ N ] next[N] next[N]数组移动指向模式串的指针匹配主串
  • 代码
int kmp(char * haystack, char * needle){
    
    const int len_ned=strlen(needle);
    if(len_ned==0)//模式串为""默认返回0
        return 0;
    const int len_hay=strlen(haystack);
    if(len_ned>len_hay)//模式串长度大于主串长度
        return -1;
    
    int next[len_ned];
    nextSolution(next,len_ned,needle);
    
    int index_hay=0,index_needle=0;
    while(index_hay<len_hay&&index_needle<len_ned){
        if(index_needle==-1||haystack[index_hay]==needle[index_needle]){//注意next[0]=-1的特例
            index_hay++;
            index_needle++;
        }else{
            index_needle=next[index_needle];
        }
        if(index_needle==len_ned){
       	 	return index_hay-len_ned;
    }
    
 	return -1;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值