内容来自刘宇波老师算法与数据结构体系课
1、KMP 算法的基本原理
border:已匹配的部分,即是前缀同时也是后缀的子串(非自身、非空),关注最长的 border


2、KMP 算法正确性的简单证明


3、什么是 LPS 数组

4、LSP 数组的计算


| LPS[i] = length(t[0 ... i] max border) |
| |
| a = LPS[i - 1],a 是前面的字符串最长的 border |
| if (t[i] != t[a]),我们可以进一步去看次长的 border、去看次次长的 border,依此类推 |
| |
| if (t[i] != t[a]) 即 红色 != 橙色,那么前面字符串最长的 border(黄色) 就匹配失败了 |
| 那么我们退而求其次,看前面字符串次长的 border,我们假设它是绿色,去比较红色和头部绿色的后一个字符(橙色),看它们是否相等 |
| 如果匹配成功了,那它就是 t[0 ... i] 最长的 border |
| |
| 头部绿色的后一个字符(橙色),它的下标是多少呢?答案是 LPS[a - 1] |
| 1、绿色是前面字符串次长的 border,那么前面绿色和后面绿色就是相等的 |
| 2、后面黄色的后缀是绿色的,前面黄色的前缀也是绿色的,同时前面黄色和后面黄色是相等的 |
| 3、绿色是黄色字符串的前缀同时也是后缀,那么绿色就是黄色的一个 border |
| 4、因为绿色是前面整个字符串次长的 border,它肯定比黄色短,同时绿色又是黄色的一个 border,那么绿色肯定是黄色字符串最长的 border |
| 5、黄色字符串的索引是 [0 ... a - 1],那么黄色字符串最长的 border 的长度就是 LPS[a - 1],即绿色的长度为 LPS[a - 1] |



5、实现 LPS 数组
1392 - 最长快乐前缀
| public class LongestPrefix { |
| |
| public String longestPrefix(String s) { |
| int[] lps = getLPS(s); |
| |
| int len = lps[s.length() - 1]; |
| return s.substring(0, len); |
| } |
| |
| private int[] getLPS(String t) { |
| |
| |
| int[] lps = new int[t.length()]; |
| |
| lps[0] = 0; |
| for (int i = 1; i < lps.length; i++) { |
| int a = lps[i - 1]; |
| |
| while (a > 0 && t.charAt(a) != t.charAt(i)) { |
| a = lps[a - 1]; |
| } |
| |
| if (t.charAt(a) == t.charAt(i)) lps[i] = a + 1; |
| } |
| |
| return lps; |
| } |
| } |
6、KMP 算法的实现
| |
| |
| |
| public class KMP { |
| |
| private KMP() { |
| } |
| |
| public static int kmp(String s, String t) { |
| if (t.length() == 0) return 0; |
| if (s.length() < t.length()) return -1; |
| |
| int[] lps = getLPS(t); |
| |
| int si = 0; |
| int ti = 0; |
| while (si < s.length()) { |
| if (s.charAt(si) == t.charAt(ti)) { |
| si++; |
| ti++; |
| if (ti == t.length()) return si - t.length(); |
| } |
| else if (ti > 0) ti = lps[ti - 1]; |
| else si++; |
| } |
| |
| return -1; |
| } |
| |
| private static int[] getLPS(String t) { |
| |
| int[] lps = new int[t.length()]; |
| |
| for (int i = 1; i < lps.length; i++) { |
| int a = lps[i - 1]; |
| while (a > 0 && t.charAt(a) != t.charAt(i)) a = lps[a - 1]; |
| if (t.charAt(a) == t.charAt(i)) lps[i] = a + 1; |
| } |
| |
| return lps; |
| } |
| } |
7、复杂度分析





8、模式匹配总结
| 动态规划的思想 |
| KMP 算法中 LPS 数组的计算:从 LPS[i - 1],推导出 LPS |

【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步