Algorithm - KMP 字符串匹配算法
2017-02-27 22:41
246 查看
前言
KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt同时发现,因此人们称它为克努特——莫里斯——普拉特操作(简称KMP算法)。KMP算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是实现一个next()函数,函数本身包含了模式串的局部匹配信息。时间复杂度O(m+n)。以上是百度百科对 KMP 的描述,在开始介绍 KMP 算法时我们先想一下如何对一字符串找到和它是否匹配的字符串。
BF 算法
暴风(Brute Force)算法是普通的模式匹配算法,BF算法的思想就是将目标串S的第一个字符与模式串T的第一个字符进行匹配,若相等,则继续比较S的第二个字符和 T的第二个字符;若不相等,则比较S的第二个字符和T的第一个字符,依次比较下去,直到得出最后的匹配结果。BF算法是一种蛮力算法。时间复杂度为 O (m * n)。代码实现:
int bfMarch(String text, String pattern) { int index = -1; int i = 0; int j = 0; int k = 0; while (i < text.length() && j < pattern.length()) { if (text.charAt(i) == pattern.charAt(j)) { i++; j++; } else { j = 0; i = ++k; } } if (j == pattern.length()) { index = i - pattern.length(); } return index; }
以上代码等同于
int bfMarch(String text, String pattern) { int index = -1; for (int i = 0; i < text.length(); i++) { for (int j = 0; j < pattern.length(); j++) { if (text.charAt(i) != pattern.charAt(j)) { break; } if (j == pattern.length() { return i - pattern.length(); } } } return index; }
KMP 算法
BF 算法比较简单,因为符合我们的逻辑思维,时间复杂度接受不了,在某种意义上为 O (m^2)。接下来我们看看另一种算法,它的时间复杂度为 O (n + m),空间复杂度为 O(m)。刚开始学习这个算法时,在网上找的资料我看的是蒙*的,说的太高深了,直到我在 B 站看了一个大神的视频,资源里有链接。我非常推荐大家先去看视频,比我描述的好多了。pattern 字符串是和 text 字符串匹配的字符串,pattern.length() <= text.length()。
KMP 的主要思想是创建一容量和 pattern 字符串一样的整型数组,里面存储与前缀相同字符的位置,如:
abcdabaa // pattern 01234567 // 数组下标 00001211 // 数组存储的值
值如何求?使用两个指针 i、j,arr[0] = 0,i 指向 arr[0],j 指向 arr[1],当判断 pattern.charAt(0) == pattern.charAt(1) 时,arr[1] = i + 1,即 0 + 1,如上列的第 3 和第 4 位,之后 i++ 和 j++。当不匹配时,则向前移一位,i 被赋值为 arr[i - 1],即 i = arr[i - 1],直到 i 变为零,如果还不匹配,则 j++。最后,j > pattern.length() 时结束。
数组的值表示,当 pattern 和 text 对比不匹配时,向前移动一位,pattern 移动的到该值表示的位置。
代码:
/** * 当字符不匹配时,可根据下标返回对应位置。 * 目的:减少匹配次数 * * @param patternStr * @return */ private static int[] computeTemporaryArray(String patternStr) { char[] pattern = patternStr.toCharArray(); int[] lps = new int[pattern.length]; int index = 0; for (int i = 1; i < pattern.length; ) { if (pattern[i] == pattern[index]) { lps[i] = index + 1; index++; i++; } else { if (index != 0) { index = lps[index - 1]; } else { lps[i] = 0; i++; } } } return lps; }
然后就开始匹配两字符串了,代码如下:
/** * 找到 pattern 在 text 第一次出现的位置 * * @param textStr * @param patternStr * @return pattern 第一个字符的位置 * -1 表示不匹配 */ private static int indexOfStr(String textStr, String patternStr) { int index = -1; char[] text = textStr.toCharArray(); char[] pattern = patternStr.toCharArray(); // KMP int lps[] = computeTemporaryArray(patternStr); int i = 0; int j = 0; while (i < text.length && j < pattern.length) { if (text[i] == pattern[j]) { i++; j++; } else { if (j != 0) { j = lps[j - 1]; } else { i++; } } } if (j == pattern.length) { index = i - pattern.length; } return index; }
完整代码
这里还有另一种写法
public class KMP { public static int KMPSearch(String txt, String pat, int[] next) { int M = txt.length(); int N = pat.length(); int i = 0; int j = 0; while (i < M && j < N) { if (j == -1 || txt.charAt(i) == pat.charAt(j)) { i++; j++; } else { j = next[j]; } } if (j == N) return i - j; else return -1; } public static void getNext(String pat, int[] next) { int N = pat.length(); next[0] = -1; int k = -1; int j = 0; while (j < N - 1) { if (k == -1 || pat.charAt(j) == pat.charAt(k)) { ++k; ++j; next[j] = k; } else k = next[k]; } } public static void main(String[] args) { String txt = "BBC ABCDAB CDABABCDABCDABDE"; String pat = "ABCDABD"; int[] next = new int[pat.length()]; getNext(pat, next); System.out.println(KMPSearch(txt, pat, next)); } }
资源
【soso字幕】汪都能听懂的KMP字符串匹配算法【双语字幕】相关文章推荐
- KMP字符串匹配算法
- KMP字符串匹配算法
- KMP字符串匹配算法
- [Algorithm] 字符串匹配: MP,KMP,暴力搜索等(ZT)
- Java实现算法导论中KMP字符串匹配算法
- KMP字符串匹配算法Java实现
- kmp字符串匹配算法
- 速写KMP字符串匹配算法
- KMP字符串匹配算法
- KMP字符串匹配算法(一)—模式字符串的匹配有限自动机
- [置顶] 字符串匹配算法KMP详细解释——深入理解
- 字符串匹配算法KMP
- KMP字符串匹配算法
- 字符串匹配算法KMP
- KMP字符串匹配算法
- KMP字符串匹配算法
- KMP string matching algorithm
- KMP-字符串匹配算法
- KMP字符串匹配算法
- KMP (Knuth-Morris-Pratt) algorithm introduction to algorithm 3rd, example 32.4