比较sub是否是str的子字符串
/**
* @auther lql
* @create 2022-09-23 14:13
**/
public class KMP {
public static void main(String[] args) {
// 比较sub是否是str的子字符串
System.out.println(kmp("ababcaababcaabc","ababcaabc" ));
}
private static boolean kmp(String str,String sub){
int[] next=kmpNext(sub);
int i=0,j=0;
while (i<str.length()){
if(str.charAt(i)==sub.charAt(j)){ // 当前字节匹配,则对比下一个字节
j++;
i++;
}else if(j!=next[j]){ // 当前字节不匹配,sub 右移至已匹配字符串前缀和后缀最长相同字符串后一位
j=next[j-1];
}else{ // sub 右移0位,[已匹配字符串前缀和后缀没有相同字符串]
i++;
}
if (j>=sub.length()) // sub匹配到最后一位提前结束循环
return true;
}
return false;
}
/**
* 计算部分匹配表["前缀"和"后缀"最长的共有元素的长度]
* @param substring
* @return
*/
private static int[] kmpNext(String substring){
int left=0,right=1;
int[] next=new int[substring.length()];
int count=0;
while (right<substring.length()){
if(substring.charAt(left)==substring.charAt(right)){
count++;
next[right]=count;
left++;
}else{ // 不匹配重头对比,重新开始累计
count=0;
if(left!=0){
left=0;
continue;
}
}
right++;
}
return next;
}
}
计算部分匹配表
"前缀"和"后缀"最长
的共有元素的长度
"前缀"指除了最后一个字符以外,一个字符串的全部头部组合
"后缀"指除了第一个字符以外,一个字符串的全部尾部组合
"ababcaabc"为例
字符串 | 前缀 | 后缀 | 匹配值 |
---|---|---|---|
a | [] | [] | 0 |
ab | [a] | [b] | 0 |
aba | [a, ab] | [ba, a] | 1 |
abab | [a, ab, aba] | [bab, ab, b] | 2 |
ababc | [a, ab, aba, abab] | [babc, abc, bc, c] | 0 |
ababca | [a, ab, aba, abab, ababc] | [babca, abca, bca, ca, a] | 1 |
ababcaa | [a, ab, aba, abab, ababc, ababca] | [babcaa, abcaa, bcaa, caa, aa, a] | 1 |
ababcaab | [a, ab, aba, abab, ababc, ababca, ababcaa] | [babcaab, abcaab, bcaab, caab, aab, ab, b] | 2 |
ababcaabc | [a, ab, aba, abab, ababc, ababca, ababcaa, ababcaab] | [babcaabc, abcaabc, bcaabc, caabc, aabc, abc, bc, c] | 0 |
KMP算法与暴力匹配算法的区别
KMP算法:16次对比+11次部分匹配表生成对比
暴力匹配算法:25次对比