KMP算法解决的问题
假设主串为S,待比较的子串为s:
朴素匹配算法:如果使用朴素匹配算法,s和S每个字符逐一比较,如果有一个字符不相同,则s的下标和S的下标都会重新退回到0,再次进行比较。
KMP算法:KMP算法就是为了子串s能够快速的移动,不要从0开始比较。
KMP算法的依据
子串s能够快速移动的依据就是已匹配的子串的前n位能否等于匹配部分的主串的后n位。如果相同,则可以跳过中间部分,从这一相同的部分再次开始比较。
KMP算法的过程
举个栗子:
假如现在S和s如下:
主串S:xxxxABDBABxxxxx
子串s:ABDBACF
开始比较:
xxxxABDBABxxxxx
ABDBABF
1. 寻找匹配串:
我们可以发现,已匹配的子串s为:ABDBAB,匹配部分的主串S:ABDBAB
2. 分析前缀后缀
前缀:A AB ABD ABDB ABDBA
后缀:B AB BAB DBAB BDBAB
3. 找最大并且相同的前后缀
我们可以发现,AB为最大相同的前后缀
4. 跳过中间部分:
移动的距离为匹配的长度-刚才的前后缀长度
也就是 6-2=4
xxxxABDBABxxxxxxxxxx
ABDBABF
KMP算法的实现
//寻找回溯的下标
public static int[] find_next(char[] s){
int i = 0; //记录遍历字符串的下标,从0开始遍历
int j = -1; //记录最大前后缀的下标,还没有开始匹配
int[] next = new int[s.length+1]; //保存下标
next[0] = -1; //初始化数组,子串中没有相同的字符
while (i<s.length)
{
if(j == -1 || s[i] == s[j]) //找到匹配字符的最大前后缀,下标继续向前找
{
++i;
++j;
next[i] = j;
}
else //没有找到,开始回溯查找
j = next[j];
}
return next;
}
//KMP匹配算法
public static int index(char[] parent,char[] children,int[] next){
int index = -1; //记录子串匹配父串的索引
int plen = parent.length;
int clen = children.length;
int i = 0; //记录遍历主串的下标
int j = -1; //记录子串需要回溯的下标
while (i < plen && j <clen)
{
if(j == -1 || parent[i]==children[j]) //匹配字符成功
{
++i;
++j;
}
else //匹配失败,根据next[]回溯
j = next[j];
}
if(j>=clen) //完全匹配成功
index = i-clen;
return index;
}
测试一下:
public static void main(String[] args) {
String parent = "xABDBAxxABDBABxxxxx";
String children = "ABDBAB";
int[] next = find_next(children.toCharArray());
int index = index(parent.toCharArray(),children.toCharArray(),find_next(children.toCharArray()));
System.out.println(index);
}
结果为8