KMP 算法
Knuth-Morris-Pratt(KMP) 算法用于搜索给定字符串中的模式。
- 首先,它在模式中找到称为LPS的重复子串,并将LPS信息存储在数组中。
- 其次,进行字符串匹配。当发生不匹配时,它利用 LPS 数组来决定从哪里开始下一个匹配,以避免多余的旧比较。
如下图
- 第一次匹配失败后,右移到已匹配部分数组的最大 前缀=后缀=a 的后缀起始,重新进行匹配
- 第二次匹配失败后,右移到已匹配部分数组的最大 前缀=后缀=aba 的后缀起始,重新进行匹配
lps 数组的定义
lps[i] 是模式 pattern,0 ~ i 的子串,前缀 = 后缀 的最大长度
lps[i] = the longest proper prefix of pat[0..i] which is also a suffix of pat[0..i].
- 如 abaabac,lps[0] = 0 (a),lps[1] = 0 (ab),lps[2] = 1(aba), lps[3] = 1 (abaa), lps[4] = 2 (abaab), lps[5] = 3 (abaaba), lps[6] = 0 (abaabac)
前缀后缀不能是整个 pattern ,最多到倒数第一个
- 如 AAAA, lps[0] = 0 (A),lps[1] = 1 (AA),lps[2] = 2 (AAA),lps[2] = 3 (AAAA)
计算 lps 数组
java 代码
public static int[] computeLPS(String pat) { int len = 0; // i 总是在 len 的前面 int i = 1; int[] lps = new int[pat.length()]; // lps[0]总是为0 lps[0] = 0; while (i < pat.length()) { if (pat.charAt(len) == pat.charAt(i)) { len++; // 匹配上的长度 lps[i] = len; i++; } else { if (len != 0) { // 不相等,下面的往右拉 // 从比 0~len(kmpkmd) 少一个字符的子串(kmpkm) 的最大前后缀 开始比较 (len=lps[kmpkm]=2(km)) len = lps[len-1]; } else { // 下面的已经到头了,不能往右拉了。只能上面的 i++ 带来新的可能性 i++; // i 加了,lps[i] 必有值 lps[i] = 0; } } } return lps; }
模式匹配
public void kmp(String pat, String txt) { int i = 0; int j = 0; int[] lps = computeLPS(pat); List<Integer> res = new ArrayList<>(); /* // 退出循环条件 * 后面这样肯定是匹配不上了(长度都不够),所以退出循环 * kmppkmpkmpkmdkmpkmpk * i * kmpkmdkmpkmpk * j */ while (txt.length()-i >= pat.length()-j) { if (txt.charAt(i) == pat.charAt(j)) { i++; j++; } if (j == pat.length()) { // 为啥是 i-j /* * kmppkmpkmpkmdkmpkmpk * i-j i * kmpkmdkmpkmpk * j */ res.add(i-j); j = lps[j-1]; } else if (i<txt.length() && txt.charAt(i) != pat.charAt(j)) { if (j == 0) { i++; } else { j = lps[j-1]; } } } System.out.print(res); }
示例
标签:pat,int,lps,len,++,字符串,匹配 From: https://www.cnblogs.com/suBlog/p/17901714.html