🍊 Java学习:Java从入门到精通总结
🍊 Spring系列推荐:Spring源码解析
📆 最近更新:2021年12月13日
🍊 个人简介:通信工程本硕💪、阿里新晋猿同学🌕。我的故事充满机遇、挑战与翻盘,欢迎关注作者来共饮一杯鸡汤
🍊 点赞 👍 收藏 ⭐留言 📝 都是我最大的动力!
KMP 算法(Knuth-Morris-Pratt 算法)是一个著名的字符串匹配算法,效率很高,说实话,有点复杂。
这种高级且常用的算法经常会在各种竞赛 / 面试场景中出现,先给大家说一个真实的例子吧,在我当初校招面试华为sp时,面试官就让我当场写一个KMP算法,大致的题目如下:
这个算法是一个比较奇葩的存在,业内流行一句话,当你第一遍没学会掌握kmp时,之后再看就会耗费成倍的精力。不幸的是我第一遍没掌握这个算法,所以当时我是直接把模板给背诵了下来,以一种非正常的方式通过了面试。
之后复盘环节偶然看到了东哥(公众号labuladong)使用动态规划的思路给出的解析,顿感豁然开朗,把之前非常诡异的next
数组定义给丢掉了,二维数组的解法不禁让我直呼内行,先整理如下,祝各位蓝桥杯顺利~
约定:pat表示模式串,长度为M,txt表示文本串,长度N为N。KMP 算法是在txt中查找子串pat,如果存在,返回这个子串的起始索引,否则返回 -1。
目前大部分的解析应该是:对pat进行一顿操作之后生成一个next数组,再利用next数组的数据去一顿操作来匹配txt。next数组和txt、pat的前缀后缀有关,很难理解,理解了可能也忘的比较快。我么可以用一个二维数组来减少代码长度,提高可解释性。
1 概述
普通字符串匹配可以用暴力破解来写,具体思路是:从txt起始索引0开始匹配pat,匹配上了则返回起始索引,匹配不上则再从txt索引1处开始匹配,以此类推…
KMP 算法的不同之处在于,它会花费空间来记录一些信息,在上述情况中就会显得很聪明。由于pat中根本不存在c字符,所以可以直接跳过这个字符:
KMP 算法则不会重复扫描txt,而是借助next数组中储存的信息把pat移到正确的位置继续匹配,用空间换时间。
传统的KMP算法框架如下:
// KMP算法主体逻辑。str是主串,pattern是模式串
public static int kmp(String str, String pattern) {
//预处理,生成next数组
int[] next = getNexts(pattern);
int j = 0;
//主循环,遍历主串字符
for (int i = 0; i < str.length(); i++) {
while (j > 0 && str.charAt(i) != pattern.charAt(j)) {
//遇到坏字符时,查询next数组并改变模式串的起点
j = next[j];
}
if (str.charAt(i) == pattern.charAt(j)) {
j++;
}
if (j == pattern.length()) {
//匹配成功,返回下标
return i - pattern.length() + 1;
}
}
return -1;
}
// 生成Next数组
private static int[] getNexts(String pattern) {
int[] next = new int[pattern.length()];
int j = 0;
for (int i=2; i<pattern.length(); i++) {
while (j != 0 && pattern.charAt(j) != pattern.charAt(i-1)) {
//从next[i+1]的求解回溯到 next[j]
j = next[j];