KMP算法-JAVA代码
(求解KMP算法的难点就在于如何求next数组,有 暴力法和 进阶版两种)
一:暴力法:求next数组时枚举所有可能的前后缀 ,找到最长的相同前后缀,将其长度存入next数组。next数组 0和1 位置上的元素初始化为-1和0;
1 public int[] getNext(String pattern){ 2 int [] next = new int[pattern.length()]; 3 next[0] = -1; 4 next[1] = 0; 5 //枚举前后缀 6 for(int i=2; i< next.length; i++){ 7 //从长到短枚举,第一个相等的前后缀就是长度最长的 8 int j = i-1; 9 for (; j>=0; j--){ 10 if(pattern.substring(0,j).equals(pattern.substring(i-j,i))){ 11 next[i] = j; 12 break; 13 } 14 } 15 //没有公共前后缀 16 if(j<0){ 17 next[i] = 0; 18 } 19 } 20 return next; 21 }
这种方法求最长前后缀容易想到但是效率低,可以进行改进。
二:进阶版:根据之前生成的next值来判断下一次前后缀比较的位置。(即边生成next数组,边利用它)
1 public int[] getNext2(String pattern){ 2 /**三个步骤:j表示最长前缀末尾(以及pattern中[0,i)子串的最大公共前后缀长度),i-1表示最长后缀末尾!!这里要理解 3 * 1.初始化; 4 * 2.当前(当前就是最长的)前后缀不相同,j要回退,查next表,j=next[j],j为0则停止。 5 * (next[j]的值就是当时在位置i=j时已经匹配的最长前后缀长度,而j从0开始,所以j=next[j]就是让j指向上一次最长前缀的后一位); 6 * 3.前后缀相同情况,更新next数组,继续向后比较; 7 */ 8 //1.初始化 9 int [] next = new int[pattern.length()]; 10 next[0] = -1; 11 next[1] = 0; 12 int j = 0; 13 //因为0,1位置没有前后缀,故i从2开始。 14 int i = 2; 15 while(i<pattern.length()){ 16 //2.不相等的情况,注意j为0时表示前缀到头了,不必再往前找,直接给next数组赋值0 17 if(pattern.charAt(i-1)!=pattern.charAt(j)){ 18 if(j==0){ 19 next[i] = 0; 20 }else{ 21 //利用已匹配成功的信息,让j指向上一次最长前缀的末尾后一位数。 22 j = next[j]; 23 } 24 }else{ 25 //3.相等的情况 26 //因为next存入的是最大前后缀长度,而j从0开始,故+1 27 next[i] = j+1; 28 //继续向后比较 29 j++; 30 i++; 31 } 32 } 33 return next; 34 }
注意:j不相等时为什么j要退到next数组对应的值处?
因为i,j对应的数不一样,而i和j前面的数是已经匹配好的,所以此时j对应的前缀和i对应的后缀不匹配,j只能再往前移,找到更小一些的前缀,而next数组对应的值就是更小的已经匹配好的前缀,j移过去之后再比较i和j的值是否相等,重复上述过程,直到j=0或者i和j对应的值相等。
求出next数组之后,kmp算法就变得非常简单了!
1 public int kmp(String str, String pattern){ 2 int[] next = this.getNext2(pattern); 3 int i=0, j = 0; 4 while(i<str.length()){ 5 if(str.charAt(i)!=pattern.charAt(j)){ 6 j = next[j]; 7 if(j==-1){ 8 i++; 9 j=0; 10 } 11 }else{ 12 if(j==pattern.length()-1){ 13 return i-j; 14 } 15 i++; 16 j++; 17 } 18 } 19 return -1; 20 }
希望有所帮助!参考:图解KMP算法 https://leetcode-cn.com/problems/shortest-palindrome/solution/tu-jie-kmpsuan-fa-by-yangbingjie/
浙公网安备 33010602011771号