著名字符串匹配算法:KMP算法原理分析和代码实现
2019-01-06 01:50
369 查看
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/m0_37264516/article/details/85892084
情况二:若情况一中的
假设
核心思想
KMP 算法,全称是 Knuth Morris Pratt 算法,核心思想与 BM 算法类似,假设现在有主串
A,模式串
B,在主串中查找模式串,在遇到不可匹配字符时,希望通过一些规律,使得模式串可以多向后滑动几位以加快匹配速度。
如图,主串和模式串在匹配时遇到坏字符时,可以发现模式串往后移动一位或两位,第一个字符便不匹配,而滑动三位则正好匹配了,因此,考虑是否存在一种规则,通过遇到坏字符时的其他已知条件来推导最大滑动位数。
KMP 基本原理
当遇到坏字符时,已知的条件有好前缀,当模式串往后移动并继续匹配时,可以发现其实就是好前缀的后缀子串与它的前缀子串在匹配,如图:
所以我们可以通过好前缀来找到一些规律,也就是要求好前缀的前缀子串与后缀子串能够匹配的,并且是最长的那个子串,即最长可匹配前缀子串,也是最长可匹配后缀子串。
在匹配时,可以通过好前缀的最长可匹配前后子串,滑动至最长可匹配前后子串对应的位置继续向后匹配:
通过列举好前缀的前后缀子串,可以发现,其实好前缀就是模式串的前缀子串,在计算最长可匹配子串时,与主串没有关系,所以,我们可以事先对模式串进行预处理,通过最长可匹配子串计算出模式串中任意一位作为坏字符位置时,模式串的滑动位数,存储这个位数的数组就是 next 数组。
KMP 算法代码:
// str, model 分别是主串和模式串;n, m 分别是主串和模式串的长度。 public static int kmp(char[] str, int n, char[] model, int m) { int[] next = getNexts(model, m); int j = 0; for (int i = 0; i < n; ++i) { if (str[i] == model[j]) {//从前往后进行匹配 ++j; } if (j == m) { // 找到匹配模式串的了 return i - m + 1; } while (j > 0 && str[i] != model[j]) { // 遇到坏字符,利用next数组计算j的位置,直到str[i]与model[j]匹配或第一个字符也不匹配时, j = next[j - 1] + 1; } } return -1; }
next 数组计算方法
暴力匹配法:将模式串每个前缀子串与其所有后缀子串进行匹配,可得到 next 数组,但是效率太低。
利用动态规划思想计算:
当我们计算 next[i] 时,next[0] ~ next[i-1] 的值已经计算出来了,思考是否可以通过已知值来计算 next[i] 的值呢?
情况一:若 next[i-1] = k-1,则表示
i-1所代表的前缀子串的最长可匹配前缀子串尾字符位置是
k-1,因此可以比较
model[k]是否与
model[i]相等,若相等,则
next[i]=k
情况二:若情况一中的
model[k]与
model[i]不相等时,该如何计算呢?
假设
model[0~i]的最长可匹配子串是
model[r~i],则
model[r~i-1]必定是
model[0~i-1]的可匹配后缀子串,所以,我们可以通过考察
model[r~i-1]的次长可匹配后缀子串对应的可匹配前缀子串
model[0~x]的下一个字符是否等于
model[i],若是,则
model[0~x]是所求的最长可匹配子串。
次长可匹配后缀子串肯定包含于最长可匹配后缀子串,而最长可匹配后缀子串与最长可匹配前缀子串对应,因此,就是求最长可匹配前缀子串的最长可匹配后缀子串。
计算 next 数组代码:
// model 表示模式串,m 表示模式串的长度 private static int[] getNexts(char[] model, int m) { int[] next = new int[m]; next[0] = -1; int k = -1; for (int i = 1; i < m; ++i) { while (k != -1 && model[k + 1] != b[i]) {//情况二 k = next[k]; } if (model[k + 1] == model[i]) {//符合情况一 ++k; } next[i] = k; } return next; }
KMP 算法代码实现
// model 表示模式串,m 表示模式串的长度 private static int[] getNexts(char[] model, int m) { int[] next = new int[m]; next[0] = -1; int k = -1; for (int i = 1; i < m; ++i) { while (k != -1 && model[k + 1] != b[i]) {//情况二 k = next[k]; } if (model[k + 1] == model[i]) {//符合情况一 ++k; } next[i] = k; } return next; }// str, model 分别是主串和模式串;n, m 分别是主串和模式串的长度。 public static int kmp(char[] str, int n, char[] model, int m) { int[] next = getNexts(model, m); int j = 0; for (int i = 0; i < n; ++i) { if (str[i] == model[j]) {//从前往后进行匹配 ++j; } if (j == m) { // 找到匹配模式串的了 return i - m + 1; } while (j > 0 && str[i] != model[j]) { // 遇到坏字符,利用next数组计算j的位置,直到str[i]与model[j]匹配或第一个字符也不匹配时, j = next[j - 1] + 1; } } return -1; }
相关文章推荐
- 多模字符串匹配算法原理及Java实现代码
- 算法 字符串匹配之朴素算法和KMP算法及JAVA代码实现
- 多模式串匹配算法:AC 自动机原理、复杂度分析及代码实现
- 字符串匹配之KMP算法思路、原理与Java实现
- 第四篇:决策树分类算法原理分析与代码实现
- 第一篇:K-近邻分类算法原理分析与代码实现
- 字符串匹配的KMP算法和朴素算法,及其python实现
- 数据挖掘:Apriori 关联规则分析算法原理分析与代码实现
- 决策树分类算法原理分析与代码实现
- 第五篇:朴素贝叶斯分类算法原理分析与代码实现
- 第十四篇:Apriori 关联分析算法原理分析与代码实现
- 算法串匹配C++实现字符串匹配的KMP算法
- Lucas-Kanade算法原理介绍及OpenCV代码实现分析
- Apriori 关联分析算法原理分析与代码实现
- 第五篇:朴素贝叶斯分类算法原理分析与代码实现
- KNN分类算法原理分析及代码实现
- 机器学习系列文章:Apriori关联规则分析算法原理分析与代码实现
- 朴素贝叶斯分类算法原理分析与代码实现
- 各种算法的C#实现系列1 - 合并排序的原理及代码分析
- 第七篇:Logistic回归分类算法原理分析与代码实现