KMP算法next数组求法

在kmp算法中,最难理解的那部分就是next数组的求解原理,今天看懂了一点点,现在记录一下

我们假设有这么一个字符串(模式串):abac......abab.......        
为方便起见,我们用数组 S 称呼这个字符串

下标0123................56789..............
abac................abab ..............
  k1 k    jj+1 


如上图所示,我们可以知道:当 j=8 的时候,next[j]=3(因为S[0]S[1]S[2] = S[5]S[6]S[7]),现在我们来求next[j+1]

  1. 如果S[k] = S[j],即S[3] = S[8],  那么 next[9] = next[j+1] = next[j] + 1 = k + 1 = 4
  2. 如果S[k] != S[j],即如上图所示的S[3] != S[8],那该怎么办呢?    下面着重讨论这个问题
由表格中的数据我们可知,由于S[k] != S[j],所以next[j+1]不可能为k+1这么简单,也就是说 next[j+1] 的长度不可能比 next[j] 再长了,所以我们只能寻找更短的相同前后缀

现在我们让 k1 = next[k](本来在代码中应该是:k = next[k],从而构成一个循环,但这里为了方便描述故而采用 k1 的说法,千万不要被误导),由于 k = 3,所以新的 k1 = next[3] = 1(前提是 next[k] 真的有相同的前后缀)(因为S[0] = S[2]),下面重点来了:

因为 S[0]S[1]S[2] = S[5]S[6]S[7] , 所以S[2] = S[7], 又因为有 S[0] = S[2],所以 S[0] = S[7],此时如果有S[k1] = S[j],那么就有:S[0]S[k1] = S[7]S[j],那么就可推断出 next[j+1] = next[k] + 1

最后我们发现 S[k1] = S[1] = b,S[j] = S[8] = b, 所以S[k1] = S[j] , 所以next[j+1] = next[k] + 1  ==> next[9] = 2

(2)上面的情况是 S[k1] = S[j],所以才有 next[j+1] = next[k] + 1, 那要是 S[k1] != S[j] 呢?又会是个什么情况

下标0123................56789..............
abac................abad ..............
 k2k1 k    jj+1 

如上面表格所示:此时 k1 = 1, j = 8, S[k1]  !=  S[j]  即:b != d, 这个时候我们继续让 k2 = next[k1] = next[1] = 0, 由于S[k2] != S[j], 即:S[0]  != S[8], 所以继续让 k3 = next[k2] = next[0] = -1

这个时候 k3 = -1,说明 next[j+1] 没有相同的前后缀,所以我们就让 next[j+1] = 0,并且重新开始计算 next[j+2] 的相同前后缀,对应的代码就是:j++; k++; next[j] = k; (因为 k 自增之后才为0,j 自增刚好用于计算下一个字符的相同前后缀)


说明:我这里只是描述了其中一种情况而已,但是已经足够用来说明next数组的求解原理了,表格中的红色字体部分可以替换成更长的字符序列,都没有任何问题,因为原理都是一样的


下面是KMP算法的完整代码:

[java]  view plain  copy
  1. /** 
  2.  * KMP算法 
  3.  * @param source 源字符串 
  4.  * @param target 模式字符串 
  5.  * @return 匹配到的第一个下标 
  6.  */  
  7. public static int kmp(String source, String target) {  
  8.     char[] s = source.toCharArray();  
  9.     char[] t = target.toCharArray();  
  10.     //计算next数组  
  11.     int[] next = getNext(target);  
  12.     int i = 0, j = 0;  
  13.     while (i < s.length && j < t.length) {  
  14.         if (j == -1 || s[i] == t[j]) {  
  15.             i++;  
  16.             j++;  
  17.   
  18.         } else {  
  19.             j = next[j];  
  20.         }  
  21.     }  
  22.     // 匹配成功,返回第一个匹配到的下标,否则返回-1  
  23.     if (j == t.length) {  
  24.         return i - j;  
  25.     }  
  26.     return -1;  
  27. }  
  28.   
  29. /** 
  30.  * 计算next数组 
  31.  * @param target 模式串 
  32.  * @return next数组 
  33.  */  
  34. public static int[] getNext(String target) {  
  35.     char[] t = target.toCharArray();  
  36.     int[] next = new int[t.length];  
  37.     //需要单独给next[0]赋值为-1,因为后面的循环最多只能给next[1]赋值,无法循环到next[0]  
  38.     next[0] = -1;  
  39.     int j = 0, k = -1;  
  40.     while (j < t.length - 1) {  
  41.         if (k == -1 || t[j] == t[k]) {  
  42.             j++;  
  43.             k++;  
  44.             next[j] = k;  
  45.         } else {  
  46.             k = next[k];  
  47.         }  
  48.     }  
  49.     return next;  
  50. }  

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值