KMP 算法 简析

字符串匹配算法,这里只做简要分析。
看了网上一些文章,但有些图很多,但我越看越懵TT。所以总结一篇尽量没有图的。
要理解这个算法,要分两步。

  1. 主串t与模式串p的匹配。
  2. 匹配过程中使用的提前处理获得的next数组。

下面对这两步简单分析。

步骤一. 主串与模式串的匹配。

一些文章侧重点在求next数组,但个人觉得串的匹配这一逻辑才是真正的核心,所以先看这个。
如下两个串(*代表主串未知长度字母):
主串T:****ababccc****
模式串P:ababe
此时匹配到串P的第五位,发现不匹配,比较笨的方法是串P后移一位,再从头比较。
如果此时,主串指针为i,模式串为j,那么就是i左移了j-1位,变成i - j + 1, j左移j位,变成0,再重新比较。
但是i和j同时回退显然没必要,之前的元素都已经知道比较过了啊,那么i可不可以不回退呢?
假设这个合理,i不回退,j回退变成了小于j的k,那我们继续比较T[i]和P[k]就行了。
那么势必要满足:

  1. i之前的元素和k之前的元素能匹配上切k!=j.
  2. 因为模式串的指针在当前元素不匹配的情况下会越来越小,所以k应该在满足条件1的情况下尽量大,防止我们漏掉某种匹配的情况。
  3. 当然也有可能没有符合条件1的k,这种情况下直接i++,j = 0,再继续匹配就行了。我们设定这种情况k = -1

那么,当T[i] != P[j]时,我们直接移动j到k,继续比较T[i]和P[k],这个j和k的对应关系,就是所谓的next数组。为什么j和k可以一一对应以及next数组的求法在当前步骤我们完全不管,就假设已经有了这么个数组了,且next[j] = k;

那么按照上面的逻辑,我们写一下kmp算法,就很简单了:

public int kmp(char[] t, char[] p){
  int i = 0;
  int j = 0;
  int[] next = new int[p.length];//next数组的求法先不看
  while(i < t.length && j < p.length){
    if(j == -1 || t[i] == p[j]){ //j = -1就是我们上面说的情况3
      i++;
      j++;
    }else{
      j = next[j];
    }
  }
  if(j == p.length){
    return i - j;
  }else{
    return -1;
  }
}

下面我们按照刚刚提到的 k 要满足的几个条件来求这个next数组。

步骤二.求/验证next数组

再复制一下刚才的话:

主串T:****ababccc****
模式串P:ababe
主串指针i,模式串指针j,模式串目的指针k。
条件1:i之前的元素和k之前的元素能匹配上。

不难发现,当匹配到i/j时不匹配时,说明前面的字符是匹配的(废话。。。),条件1也就转成了和主串无关的: j之前的元素和k之前的元素能匹配上。

注意这里用到匹配这个词,因此,求next数组,就变成了模式串自己匹配自己的过程。
既然都是匹配,那逻辑自然和步骤1中的逻辑差不多,只不过目的不同。

  1. 初始next[0] = -1,这个回看步骤1可以理解,这里不赘述.
  2. 当P[j] == P[k]时,j+1之前的元素和k+1之前的元素能匹配上,next[j + 1] = k + 1,同时两个指针3都可以后移。
  3. 虽说我们这里也是匹配,但肯定不能求出一个next[m] = m的结果啊,这不死循环了么。为了确保next[m] < m,我们k指针的初始设为j-1 = -1;
  4. 当P[j] != P[k]时,应该怎么办呢?回看最上面的步骤1啊,k回退成next[k]后再做比较不久行了么?什么,步骤一里面没有提供这个next数组的方法?下面我们分析一下看看是不是真的没有。
    a. P[j] == P[k]时 j和k同时+1且可以计算出next [j+1],而不匹配时k左移,所以说明再任何情况下,k<= j.
    b. 模式串和主串都是同一个,说明此时我们正在计算的next数组,就是上面我们需要的next数组啊,而我们所需要的next[k],上面的条件a说明我们已经计算出来了,因此求next数组的逻辑就是一次动态规划,直接拿来吧你!
    所以,步骤二我们求next数组直接抄袭步骤一稍作修改即可
public int[] getNext(char[] p){
  int j = 0;
  int k = -1;
  int[] next = new int[p.length];
  while(j < p.length - 1){
    if(k == -1 || p[j] == p[k]){
      next[++j] = ++k;
    }else{
      k = next[k];
    }
  }
  return next;
}

到这里,我们的next数组就求出来了。
那么,这个数组是否有优化的空间呢?

步骤三.next数组优化

这一部分我参考了一篇大佬的文章:https://www.cnblogs.com/dusf/p/kmp.html
归根结底,我们计算next数组是为了匹配字符串,如果当前两个字符不匹配,通过next数组修改j指针为k后,发现P[j] == P[k],那么P[k]和T[i]不还是不匹配么,害得再跳。而这种情况在我们计算next数组时就可以避免。因此上面求next数组的代码可以优化下:

public int[] getNext(char[] p){
  int j = 0;
  int k = -1;
  int[] next = new int[p.length];
  while(j < p.length - 1){
    if(k == -1 || p[j] == p[k]){
      if (p[++j] == p[++k]) { // 当两个字符相等时
          next[j] = next[k];//提前帮你跳了,省的匹配的时候不符合在跳,而k<j,所以这个next[k]已经是计算好的了,不会再出现p[j] == p[next[k]]的情况了。
      } else {
          next[j] = k;
      }
    }else{
      k = next[k];
    }
  }
  return next;
}

到此为止,感谢~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,142评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,298评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,068评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,081评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,099评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,071评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,990评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,832评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,274评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,488评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,649评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,378评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,979评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,625评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,643评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,545评论 2 352

推荐阅读更多精彩内容