参考 https://www.cnblogs.com/imzhr/p/9613963.html, 但是这个里面很多图都不见liao
kmp 理论就不介绍了,主要分为两部分:
(1)如何求next数组
next数组就是模式串前缀和后缀的最大公共长度,next[i]表示的是前 i 个字符的最大公共前后缀长度(前后缀都是不包括自身的)
1.next[0]=-1, next[1]=0。 2.在求解next[j]时,令k=next[j-1], 3.比较T[j-1]与T[k]的值, a. 若T[j-1]等于T[k],则next[j]=k+1。 b. 若T[j-1]不等于T[k],令k=next[k],若k等于-1,则next[j]=0,否则跳至3。
以主串“abcabaabaabcacb” 和模式串 “abaabcac”为例:
模式串T :
其中next[0] = -1 next [1] = 0 为固定的,也很好想, 当 i = 0 时,前面没有字符,自然为 -1 , 当 i = 1时,前后缀都为空,长度为0
当 i = 2 时,看的是ab 的前后缀最大公共长度 前缀 : a 后缀 : b 公共长度为0 ,用上面的流程来看
k= next[1] = 0 T[0] 和 T[1] 不相等 所以 k = next[ 0] = -1 next[2] = 0
当 i = 3 时,k = next [2] = 0 T[0] 和 T[2] 相等 next[3] = k+1 = 1;
当 i = 4 时,k = next [3] = 1 T[1] 和 T[3] 不相等 k= next[1] = 0 T[0] 和 T[3] 相等 next[4] = k+1 = 1;
当 i = 5 时,k = next [4] = 1 T[1] 和 T[4] 相等 next[5] = k+1 = 2;
当 i = 6 时,k = next [5] = 2 T[2] 和 T[5] 不相等 k= next[2] = 0 T[0] 和 T[5] 不相等 k= next[0] = -1 next[6] =0;
当 i = 7 时,k = next [6] = 0 T[0] 和 T[6] 相等 next[7] = k+1 = 1;
所以next 数组 为
(2)如何利用next数组进行字符串匹配
当主串匹配到第 i 个字符, 模式串T匹配至第 j 个字符时匹配失败,i指针不变, 将j指针置为next[j]的值,若j的值为-1,则将i和j同时加1。随后继续进行逐个的比较。
代码实现:
(1)求next 数组
public static void next(String s, int[] next){
next[0] = -1;
next[1] = 0;
for(int i = 2; i < s.length(); i++){
int k = next[i-1];
if(s.charAt(k) == s.charAt(i-1)){
next[i] = k+1;
}else{
while(k >= 0 && s.charAt(k) != s.charAt(i-1)){
k = next[k];
if(k == -1){
next[i] = 0;
}
}
next[i] = k+1;
}
}
}
(2)匹配
public static int kmp(String s,String p){
int[] next = new int[p.length()];
next(p,next);
int i = 0, j = 0;
/*
while(i < s.length() && j < p.length()){
if(s.charAt(i) == p.charAt(j)){
i++;
j++;
}else{
j = next[j];
if(j == -1){
i++;
j++;
}
}
}
//这一段是一开始完全按照匹配流程来的 , 可以改写成下面的
*/
while(i < s.length() && j < p.length()){
if(j == -1 || s.charAt(i) == p.charAt(j)){
i++;
j++;
}else{
j = next[j];
}
}
return j == p.length() ? i-j : -1;
}