public class KMP {
public static void main(String[] args) {
String mainStr = "aabaabaaf"; // 定义主串
String patt = "aabaaf"; // 定义模式串
int KMP_result = new KMP().indexOf(mainStr,patt);
System.out.println(KMP_result); // 3
System.out.println(KMP_result==mainStr.indexOf(patt)); // true
}
public int indexOf(String str, String patt){
// 求patt在str中的索引,功能同:str.indexOf(patt)
// 如果模式串为空,返回0
if (patt == "") return 0;
// 无法匹配,返回-1
if (str.length()<patt.length()) return -1;
// 构建next数组
int[] next = new int[patt.length()];
createNext(next,patt);
int i = 0; // i为主串索引
int j = 0; // j为模式串索引
while (i<str.length()&&j<patt.length()){
if (str.charAt(i)==patt.charAt(j)){
// 若相等,i和j索引同时后移
i++;
j++;
}else if (j==0) {
// 若不相等,且j==0,只后移主串索引
i++;
}else {
// 若不相等,且j!=0,则主串索引不动,模式串索引改为next[j]
j = next[j];
}
}
// 有三种情况会导致跳出上面的while循环,
// 情况1:j==patt.length()
// 情况2:j==patt.length() 的同时 i==str.length()
// 情况3:i==str.length()
if (j == patt.length()){
return i-j; // 情况1或2,匹配成功
}else {
return -1; // 情况3,匹配失败
}
}
public void createNext(int[] next, String patt){
// 计算next数组,如:next[i]表示patt[:i]的最长前后缀
// a a b a a f
// -1 0 1 0 1 2
// 初始化
int j = 0;
next[0] = -1;
if (next.length==1) return;
next[1] = 0; // patt[:1] (即,patt[0]) 没有子串
// 从i=2开始遍历模式串的索引,将结果存入next[i]
// j指向前缀的最后一个字符,i-1指向后缀的最后一个字符
// 每次for循环的目的是寻找patt[:i]的最长公共前后缀
// 进一步说,每次for循环要定位j的值,使得patt[:j+1]与patt[i-j-1:i]为最长公共前缀和后缀
// i-j-1不重要,可以忽视它,这句话可改为:使得patt[:j+1]与patt[~:i]为最长公共前缀和后缀
// 定位j的值后,意味着patt[:i]存在公共前缀和后缀,并将next[i]赋值为j++(即,公共前后缀的长度)
// 若patt[:i]不存在公共前缀和后缀,此时j=0,将next[i]赋值为j
for (int i=2; i<next.length; i++){
// 以i=10为例,这次for循环的目的是寻找patt[:10]的最长公共前后缀
// 代码运行到这里时,如果j是>0的(以j=4为例),说明patt[:4]和patt[~:9]数值上是相等的
// 此时需要判断patt[4]是否=patt[9] (即,判断patt[j]是否=patt[i-1])
while (j>0 && patt.charAt(j)!=patt.charAt(i-1)){
// 如果patt[4]!=patt[9],则通过while循环继续比较patt[next[j]]是否=patt[9]
// 此处需要多想,脑子里形成动画
j = next[j];
}
// 如果patt[4]==patt[9],那说明patt[:5]和patt[~:10]数值上相等,最长前后缀为5
// 此时j=4,先j++,然后next[10]=j
if (patt.charAt(i-1)==patt.charAt(j)){
// 代码运行到这里时,如果j是=0的,只需要判断patt[0]是否=patt[9]即可
// 如果patt[0]==patt[9],next[10]=1,否则,next[10]=0
j++;
}
next[i] = j;
}
}
}
KMP算法-附详细注释
于 2024-01-15 15:13:42 首次发布