KMP算法——next数组

本文深入讲解了KMP算法的核心——部分匹配表(PMT)的构建原理及其在字符串匹配中的应用。通过实例演示如何利用PMT数组减少不必要的字符比较,提高搜索效率。

KMP算法是一种动态规划算法,其主要用于字符串匹配。

KMP算法之所以难理解,就是其最核心的内容PMT数组难以理解。

在这里插入图片描述

下面的value就是PMT数组,为什么PMT数组是这样的呢?

首先要说明的是一个字符串的前缀后缀的问题

前缀:如果字符串A和B,存在A=BS,其中S是任意的非空字符串,那就称B为A的前缀。

例如,”Harry”的前缀包括{”H”, ”Ha”, ”Har”, ”Harr”}

后缀:如果字符串A和B,存在A=SB, 其中S是任意的非空字符串,那就称B为A的后缀。

例如,”Potter”的后缀包括{”otter”, ”tter”, ”ter”, ”er”, ”r”}

知道前缀后缀的定义后,就可以看PMT数组的定义了。

PMT数组:PMT中的值是字符串的前缀集合与后缀集合的交集中最长元素的长度。

对于字符串”ababa”,它的前缀集合为{”a”, ”ab”, ”aba”, ”abab”},它的后缀集合为{”baba”, ”aba”, ”ba”, ”a”}, 两个集合的交集为{”a”, ”aba”},其中最长的元素为”aba”,长度为3。

char:ababa
index:01234
value:00123

这样看就可以很清楚的看懂PMT数组的意义了,可以看到PMT数组实际就是一个动态规划数组,但是KMP算法要借助PMT数组干嘛呢?

我们假设有两个相同字符串"abababca",一个从第二个字符开始到最后一个字符,这里称为字符串str1,另一个从第一个字符开始到倒数第二个字符,这里称为str2。我们从这个顺序开始比对。我们在编程时为了方便,将PMT数组称为next数组,让next[0]=-1next[i]=j,这里的i可以理解为str1的第i个字符,j可以理解为str2的第j个字符,当i-1和j-1索引指向的字符相等时,状态转移方程很容易写出来

n e x t [ i ] = j + 1 i f   s t r 1 [ i − 1 ] = = s t r 2 [ j − 1 ] next[i]=j+1\quad if\ str1[i-1]==str2[j-1] next[i]=j+1if str1[i1]==str2[j1]

但是当str1[i-1]≠str2[j-1]时的转移方程呢?这就需要我们弄明白数组怎么回溯,回溯回去的状态到底代表什么了。PMT数组记录前缀和后缀的相同子串的目的,就是为了回溯的时候我们能够利用曾经比对过后得到的信息。如图©中,我们在next数组中记录了str1在从第2-4个字符代表的后缀字符串中,与str2的从第1-2个字符代表前缀字符串中的最大子串长度,我们知道接下来要从j=2处开始比较str2字符串的元素与str1中i=4后面的元素。这有什么用呢?

再来看图(d)和(e),当我们比对到i=7,j=5时,我们发现了此时str1[i-1]≠str2[j-1]了,虽然就结果来看next[7]=0但是我们不能直接令next[7]为0,因为会存在如图(d’)这样的状态,我们需要利用前面记录的PMT数组去回溯。我们观察图(d),str2被比对了第1-4个连续的前缀字符,那么就意味着此时的str1的第3-6个字符与前缀的第1-4个字符是相同的,因此我们可以不看str1的前两个字符,把第3-6个字符当作是第1-4个字符,因此很明显,可以直接回溯到next[4]的状态,令j=next[j-1](前面说了此时j=5),因此j回溯到了2,即利用了图©状态时我们已知的信息,达到了图(d’)的状态,此时再比对i=7,j=3,str1[6]≠str2[2]因此继续往前回溯j=next[j-1](此时j=3),因此j回溯到了0的状态,即图(a)的状态,此时next[i]=next[j]此时比对i=7,j=1不同,此时就得到next[7]=0了。


在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

附上计算next数组的代码

 public void countNextArray(String s){
  int n = s.length();
  char[] c = s.toCharArray();
  int[] next = new int[n + 1];
  next[0] = -1;
  next[1] = 0;
  int j = 0;
  int i = 2;
  while (i <= n) {
      if (j == -1 || c[i - 1] == c[j]) {
          next[i] = j + 1;
          j++;
          i++;
      } else {
          j = next[j];
      }
  }
}

当我们有了next数组指示我们去匹配字符串的时候,我们匹配出第一个错误字符时,就能知道str1的后缀字符串中的前缀字符串有哪些可以直接和str2的前缀字符串进行匹配,就可以减少匹配次数。因为如果用遍历的方法去匹配,必须一个字符一个字符的后移匹配,而kmp算法让我们可以记住前面匹配过的字符串,从而可以快速的匹配。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值