本文主要是介绍TMD ,KMP!,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
以为搞清楚了BM算法的思想,就容易理解KMP了,shit!还是很难理解关于KMP算法中next数组是如何高效获得的,既然如此暂时不追究了,不过已然不妨碍把代码搞清楚!
跟BM算法类似,KMP算法中主串和模式串的比对也有类似的东西如:坏字符和好前缀。
没错,就是好前缀,BM算法中是好后缀,KMP算法中是好前缀!
当遇到坏字符的时候,模式串依然是需要往后移动,对于KMP算法来说依然是设法一次滑动多个位置,而不是一个字符一个字符去地低效地比较。
其实就是在好前缀本身的后缀子串中,查找最长的能跟好前缀的前缀子串匹配的串。
把好前缀的所有后缀子串中,最长的可匹配前缀子串的那个后缀子串叫做最长可匹配后缀子串;对应的前缀子串叫做最长可匹配前缀子串。
如何求解好前缀的最长可匹配前缀子串和后缀子串?这个问题其实只跟模式串本身有关系,既然是好前缀,那么模式串中也一定包含好前缀,那么问题就转化为:求模式串好前缀的最长可匹配前缀子串。
在KMP算法中可以事先根据模式串本身来构建一个存储最长可匹配前缀子串的数组,这个数组的下标为模式串前缀结尾字符的下标(也就是好前缀的候选字符串),值为这个前缀的最长可匹配前缀子串的结尾字符下标。如:
这个next数组我们称之为“失效函数”,失效函数如何计算出来,一个简单的方法是:如图想求出前缀b[0,4]即ababa这个前缀的最长可匹配前缀的结尾字符下标,可以把b[0,4]的后缀子串全部列举出来跟b[0,4]的前缀去匹配,这样就可以得到最长的可匹配前缀的结尾字符,如图就是ababa的最长前缀子串是aba,结尾字符下标就是2,于是next[4]=2.
但是这种方式求next数组的值看起来比较低效,高效的方法有,极客时间争哥的课程第34节里有,可惜我没看懂!感觉变量太多了,我是看得绕进去了,感兴趣可以自己去看下,争哥的教程很棒!
下边我就假设next数组已经是被填充的,直接把代码拿上来看看:
package com.study.algorithm;/*** KMP算法* @author jeffSheng* @date 20191001**/
public class Kmp {public static void main(String[] args) {String a = "ababaeabac"+"ababacd"+"xxxx";String b = "ababacd";int x = kmp(a.toCharArray(),a.length(),b.toCharArray(),b.length());System.out.println(x);}// a, b 分别是主串和模式串;n, m 分别是主串和模式串的长度。public static int kmp(char[] a, int n, char[] b, int m) {int[] next = getNexts(b, m);int j = 0;for (int i = 0; i < n; ++i) {while (j > 0 && a[i] != b[j]) { // 一直找到 a[i] 和 b[j]j = next[j - 1] + 1;}if (a[i] == b[j]) {++j;}if (j == m) { // 找到匹配模式串的了return i - m + 1;}}return -1;}// b 表示模式串,m 表示模式串的长度private static int[] getNexts(char[] b, int m) {int[] next = new int[m];next[0] = -1;int k = -1;for (int i = 1; i < m; ++i) {while (k != -1 && b[k + 1] != b[i]) {k = next[k];}if (b[k + 1] == b[i]) {++k;}next[i] = k;}return next;}}
代码我试过了,正常得出结果,对于kmp这个方法可以做如下图解:
虽然没有搞清楚next数组的高效计算方式,但是理解了整体的思想,我觉得也是够了,能够在实际软件开发中用得上也就可以了,我不觉得非要把那些细节搞清楚!TMD!KMP!
这篇关于TMD ,KMP!的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!