26、字符串匹配 KMP 算法
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 数组
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) {
// border: 即是前缀又是后缀的子串(非自身、非空)
// lps[i] 代表 t[0 ... i] 最长的 border 的长度
int[] lps = new int[t.length()];
lps[0] = 0;
for (int i = 1; i < lps.length; i++) {
int a = lps[i - 1]; // a 代表 t[0 ... i - 1] 最长的 border 的长度
while (a > 0 && t.charAt(a) != t.charAt(i)) {
a = lps[a - 1]; // a 代表 t[0 ... i - 1] 次长的 border 的长度
}
if (t.charAt(a) == t.charAt(i)) lps[i] = a + 1;
}
return lps;
}
}
6、KMP 算法的实现
/**
* 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; // sIndex
int ti = 0; // tIndex
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) {
// lps[i] = length(t[0 ... i] max border)
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
本文来自博客园,作者:lidongdongdong~,转载请注明原文链接:https://www.cnblogs.com/lidong422339/p/17329329.html