最近学习在字符串匹配时,会用到KMP算法,比BF(暴力)算法性能提高了许多,于是用Java实现了一下。
算法中主要问题就是求得next数组,在这里也出现了可以优化的地方,算法原理讲解很多博客也给出,代码供上。。。
写的不好,请指出问题。
package algorithm; public class KMR { /** * 记m=match.length() * 此算法为未改进的KMP算法中的求next数组 * next[j]表示array[0]~array[j-1]中真前缀和真后缀相等的最大长度,记为k * 在求next[j]时,对array[0]~array[j-1]进行了j-1躺搜索 * 算法复杂度为O(m^3) * */ public static int[] getNext(String match) { int[] next=new int[match.length()]; next[0]=-1; int i,j,len; for(j=1;j<match.length();j++) // 相等子串的从match[0]-match[j-1]查找 { for( len=j-1;len>=1;len--) { for( i=0;i<len;i++) // 依次比较match[0]-match[len-1] 与 match[j-len]-match[j-1] { if(match.charAt(i)!=match.charAt(i+j-len)) break; } if(len==i) { next[j]=len; break; } } if(len<1) next[j]=0; } System.out.print(match+"的next[]数组为:"); for(int interger:next) System.out.print(interger+" "); return next; } /* * 此算法为改进过的KMP算法中的求next数组 * next[j]表示array[0]~array[j-1]中真前缀和真后缀相等的最大长度,记为k * 我们会发现 * if(array[j]==array[k]),那么next[j]自然等于k+1,相当于原来的前缀和后缀都添加上了相同的一个字符 * if(array[j]==array[k]), 那么我们需要去求array[0]~array[j-1]中真前缀和真后缀相等的第二长的长度 * 记为k'=next[k] 再判断if(array[j]==array[k']) * 一直做同样的操作,直到array[j]==array[k'''']或*next[k''''']=-1停止 * 算法复杂度为O(m)*/ public static int[] advancedGetNext(String match) { int[] next=new int[match.length()]; next[0]=-1; // 初值,每个字符串的第一个字符一定不存在最大前缀和最大后缀 int j=1; int k=-1; while(j<match.length()) { if(k==-1) { next[j]=0; j++; k=next[j-1]; // 提前算好下一次迭代时的k } else if(match.charAt(j-1)==match.charAt(k)) { next[j]=k+1; j++; k=next[j-1]; } else { k=next[k]; } } System.out.print(match+"的next[]数组为:"); for(int interger:next) System.out.print(interger+" "); return next; } public static int KMP(String str,String match) { int[] next=advancedGetNext(match); // int[] next=getNext(match); int i=0,j=0; while(i<str.length()&&j<match.length()) { if(str.charAt(i)==match.charAt(j)) { i++; j++; } else { j=next[j]; if(j==-1) // 子串的最大前缀和最大后缀不存在 { i++; j++; // 这条语句也可以写成j=0; } } } if(j==match.length()) { return i-match.length(); } else { return -1; } } public static void main(String[] args) { String str="ababaababcb"; String match1="ababc"; String match2="abcd"; int index1=KMP(str,match1); System.out.println("\n"+match1+"在"+str+"中的起始索引号为:"+index1); int index2=KMP(str,match2); System.out.println("\n"+match2+"在"+str+"中的起始索引号为:"+index2); } }