这个算法还是很强大和牛逼的,能想到这样的算法真的很厉害。
我这里就不介绍它是干嘛的,我直接写上它的思路和实现代码
1:kmp算法基本思路
主方法中两个String topic_str ; 和 String pattern_str ;
topic_str和 pattern_str 进行遍历比较
一个while循环条件为 j<pattern_str.length|| i<topic_str.length
里面进行判断如果
pattern_str.charAt(j)==topic_str.charAt(i)
那么
i++;j++;
否则
j=next[j];
/**
* 对主串s和模式串t进行KMP模式匹配
*
* @param s 主串
* @param t 模式串
* @return 若匹配成功,返回t在s中的位置(第一个相同字符对应的位置),若匹配失败,返回-1
*/
public static int kmpMatch(String s, String t) {
char[] s_arr = s.toCharArray();
char[] t_arr = t.toCharArray();
int[] next = getMySelfNextPlus(t_arr);
int i = 0, j = 0;
while (i < s_arr.length && j < t_arr.length) {
if (j == -1 || s_arr[i] == t_arr[j]) {
i++;
j++;
} else
j = next[j];
}
if (j == t_arr.length)
return i - j;
else
return -1;
}
核心构建next[j] 数组
基本思路如下 next数组代表的是 pattern_str 中各个字节回退的index值 例如 "abcabd" 中 'd' index=5 next[5]=2 所以再进行比较中直接回退到 'c'
关键在于两个索引遍历字符串 pattern_str (k,j)
初始化时候:
k位于-1位置,j位于1位置,next[0]=-1, next[1]=0; 理解如下:next[0]只有一个元素,说明没有可以回退位置所以到达-1(-1代表的是没有比较的所以主串i需要+1,j为0),
next[1]前面只有一个字符,无法比较所以只能为0; 例如"abcabd"中比较第一个'b' 时候 不相等进行回退,我们只能回退到'a' next[1]=0
比较的逻辑:
记住,k代表的是 pattern_str中的前缀当前位置 ,j 代表后缀当前位置 我们计算的next[j] 都是j位置之前的前后缀位置值。例如"abcabd" 'd'位置的next[j] 算法取得前后缀为"abcab" j,k均在两个'b'上。
if(pattern_str[k]==pattern_str[j])时候:j自增1 ,k自增1.此时next[j] = k;(这里想法是当我们jk位置值相等,我们下一个位置如果要回退说明不等,那么我就可以直接去k的位置拿值所以是next[j]=k)
else时候:k=next[k];(这里是整个算法最难理解的地方,我们转换思路,kmp是用来匹配字符串的,那么我们是不是可以把k和j当成kmp算法基本思路中的i和j呢?
那么是不是可以那它的前缀也就是k的位置当作 pattern_str 而j那里当作 topic_str。这样一看是否就是只需要走代码中j=next[j]这一步即可把k回退到next[k]然后重新匹配)
public static int[] getMySelfNext(char[] p) {
//next的含义就是代表上一个匹配的前后缀的位置。
int[] next = new int[p.length];
int k = -1, j = 1;
//初始化k代表没有匹配,j代表指向第二个字符。
next[0] = -1;
next[1] = 0;
while (j < p.length - 1) { //因为我们判断的是下一个next[j+1]的值所以j<p.length-1
if (k == -1 || p[j] == p[k]) {
j++;
k++;
next[j] = k;
} else {
//这里的思想其实就是,k=next[k] 这里我们可以理解为字串p0--pk 以及 主串pj-k---pj这一块进行kmp算法来匹配。
//kmp回退思想所以k回退然后继续。
k = next[k];
}
}
return next;
}
2.kmp算法plus
上面的算法存在一些缺陷主要在于next数组存在问题例如当 pattern_str[j]=k这一步我们默认的是当j,k自增后也就是下一个位置如果不相等才会回退,才会到next[j]那时候直接就去k位置的值,但是问题是假设相等呢?
如果相等的话,而且又需要回退说明k位置已经不对了所以我们应该去next[k]位置也就是 next[j]=next[k] 因为k不对那么我就去next[k]所以进行if判断后当if=true时候且k,j都自增1到达指定位置时候:
我们再加个判断if(p[k]==p[j])为真的时候:next[j]=next[k]
否则next[j]=k;
public static int[] getMySelfNextPlus(char[] p) {
//next的含义就是代表上一个匹配的前后缀的位置。
int[] next = new int[p.length];
int k = -1, j = 1;
//初始化k代表没有匹配,j代表指向第二个字符。
next[0] = -1;
next[1] = 0;
while (j < p.length - 1) { //因为我们判断的是下一个next[j+1]的值所以j<p.length-1
if (k == -1 || p[j] == p[k]) {//我们首先判断j和k位置是否匹配
j++; //匹配则判断下一个位置也就是我们next的位置的值是否相当若不相等,则next的位置值为k的下一个位置值,若相等则我要回退到k那里,但k那里相等所以我只能去next[k]那个新位置。
k++;
if (p[j] == p[k]) {
next[j] = next[k];
} else {
next[j] = k;
}
} else {
//这里的思想其实就是,k=next[k] 这里我们可以理解为字串p0--pk 以及 主串pj-k---pj这一块进行kmp算法来匹配。
//kmp回退思想所以k回退然后继续。
k = next[k];
}
}
return next;
}