KMP算法代码及优化(详解)

有点长,请耐心看完

在上一篇博文中了解了BF算法的代码,BF(Brute-Force)算法从它的英文简称都可以知道是靠蛮力的的匹配关键字的方法,其实就是将模式串与主串的第一个(按照书上的说法,也可以不一定是第一个)依次一个一个的往后面比较,注意,我说的是主串的每一个,也确实够蛮力的。

我们来回顾一下:

  • 先声明一下:我们称主串为s,i是它的指针;模式串为t,j是指针,其实就是他们的序列位数,因为数组的位数是从零开始的,所以我们的字符串的第1个我们称它为第0位
if (s[i] == t[j])
        {
            i++;
            j++;
		}
        else
		{
			i = i-j+2;//其实就是继续比较主串的后一个字符,就是说可以另设一个参数从0开始,然后失配一次就加 1。(此处是为了和课本上统一) 			
			j = 1;	
		}     	

KMP算法同样也是查找关键字的算法,算是BF算法的改进版吧,因为BF中的主串指针回溯问题被巧妙的解决了,其实大家也都知道就是利用课本上前后缀这个概念创造了一个 next 数组(书本上是函数,我更喜欢叫他它数组)。

再来对比看一下KMP与BF的区别:

if (s[i] == t[j])
        {
            i++;
            j++;
		}
        else
		{
			j = next[j];//与BF的区别
		}     	

我们马上明白了,我们可以用next[j] 来表示 j 的下一次位置,那么 next[] 这个数组怎么求呢?

int len_t = strlen(t) - 1;
   next[0] = -1;
   int j = 0;
   int k = -1;
   while (j < len_t)
   {
      if (k == -1 || t[j] == t[k])
      {
          if (t[++j] == t[++k]) //就这一个判断就是书上说的称为next函数的优化
             next[j] = next[k];//这一步很难理解
      }
     else
          k = next[k]; //这一步很难,但是认真去演示一下也能理解 
    }

自己在草稿纸上任意写个串演示一下就会很清楚了;
注意:我刚刚说的,我们的 j 是从0开始的;

  • if(t[++j] == t[++k]) next[j] = next[k] 这一步,这是对next函数的优化,注意next[j] = k 这个概念,是说k就是 j的下一次位置,当我们发现像 ABAB 这种串,当j移动到第3位(字母“B”)时,若此时才失配,而next[j]=1,也就是第2个B的位置,可是刚刚我们就是因为B而失配的,所以j就算移动下一位置还是失配,那我们就直接移动到下一位置的的下一位置即当t[++j] == t[++k])时,next[j] = next[k]
  • k = next[k]这一步,从程序中看出能执行这一步那是因为if (k == -1 || t[j] == t[k])不满足,可是,在j前面的肯定有满足的,有点递归的思想,然后前后又分成两个串来看,就找到了。

其实就是当前已经求到next[j],接着就应该求解next[j+1],此时就分两种情况,一种是:重复的字符串个数会增加,即所谓的p[k]=p[j],此时p[j+1]=k+1;即p[++j]=++k;另一种就是不能增加,也就是说P[k]!=P[j],即最大重复子串的长度不能增加了

现在再来看完整的代码
我希望看的时候能够从主函数开始从下往上看:

#include<stdio.h>
#include<string.h>  //提供strlen()函数计算字符串的长度
// KMP最核心之处,就是求课本上求next[]数组如下:
int getnext(char *t, int next[]) //自己定义一个 getnext()函数
{
    int len_t = strlen(t) - 1;
    next[0] = -1;
    int j = 0;
    int k = -1;
    while (j < len_t)
    {
         if (k == -1 || t[j] == t[k])
         {
              if(t[++j] == t[++k])
              next[j] = k;
         } 
         else
         {
              k = next[k]; //这一步很难,但是认真去演示一下也能理解 
         }
    }
// 打印出每一个next[j]的值,并不一定需要。(我只是为了美观,,满足我的强迫症) 
    int i;
    for (i = 0; i <= len_t; i++)
       {
            printf("%d ", next[i]);
       }
    printf("\n");
}
//KM与BF的不同之处:
int kmp(char *s, char *t, int next[])
{
  int i = 0, j = 0;
  int len_s = strlen(s) - 1;
  int len_t = strlen(t) - 1; //strlen()函数计算长度时不会算上,但是匹配的时候数组末尾含"\0",所以要减 1。(想了好久555) 
  while (i <= len_s && j <= len_t)
  {
     if (s[i] == t[j])
     {
         i++;
         j++;
    } 
    else
     {
          if (next[j] != -1) //最初我一直以为需要去定义一个next函数 
         {
             j = next[j]; //KMP优势,相比BF,i不用回溯了 
         } 
         else
        {
            i++;
            j = 0;
        }
     }
  }
  if (j > len_t)
  return i - len_t;
  else
  return -1;
 }

int main()
{
   int next[50];
   char *str1 = "lx love zlr";
   char *str2 = "love";
   int len_s = strlen(str1) - 1; //strlen()函数计算长度时不会算上,但是匹配的时候数组末尾含"\0",所以要减 1。
   int len_t = strlen(str2) - 1; //下面不再赘述  
   printf("next[j]的值:");
   
   getnext(str2, next); //调用getnext()函数,然后求出next[]数组(想了很久...) 
   printf("\n关键字出现的位置是:");
   
   printf("%d", kmp(str1, str2, next));//再去看kmp函数
   return 0;
}

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

刘学.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值