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/

posted @ 2021-03-09 20:33  Only、  阅读(132)  评论(0)    收藏  举报