KMP
源串S,模式串P,返回源串中包含的模式串的第一个字符下标
KMP是借用next[]数组将字符串匹配问题的复杂度由暴力求解法的O(m*n)降为**O(m+n)**的算法
暴力解法是用两个指针将模式串与源串字从第一个字符开始匹配,如果相同匹配下一个字符,当匹配到一个不相同的字符时,源串指针回溯到第二个字符再次与模式串第一个字符进行匹配
KMP算法只回溯模式串不对源串进行回溯,当匹配到不相同的字符时,只需要从next[]数组中得到最长匹配数组即模式串应该回溯的位置再进行匹配
next[]数组的求法
对于模式串P,next[0] = -1;j为模式串中的第几个字符;k为该字符next中对应的值
如果Pj==Pk,next[j+1] = k + 1;j++;k++;
否则,k继续回溯,直到满足Pj==Pk或k<0为止;
public class 字符串匹配_KMP {//O(n+m)
public static void main(String[] args) {
String s = "nnabababa";
String p = "abab";
System.out.println(indexOf(s, p));
}
/**
* @param s源串
* @param p模式串
* @return
*/
private static int indexOf(String s,String p){
if(s.length()==0||p.length()==0) return -1;//如果为空串返回-1
if(p.length() > s.length()) return -1;//如果源串长度小于模式串,返回-1
int [] next = next(p);
int i = 0;//s位置
int j = 0;//p位置
int slen = s.length();
int plen = p.length();
while(i<slen){
//如果j = -1,或者当前字符匹配成功(即S[i] = P[i]),都令i++,j++
//j=-1,因为next[0] = -1,说明p的第一位和i这个位置无法匹配,这时i,j都增加1,i移位,j从0开始
if(j==-1||s.charAt(i) == p.charAt(j)){
i++;
j++;
}else{
//如果j != -1,且当前字符匹配失败(即S[i] !=p[j]),则令i不变,就= next[j]
//next[j]即为j所对应的next的值
j = next[j];
}
if(j == plen){//匹配成功
return (i-j);
}
}
return -1;
}
/**
* 构造next数组
* @param ps
* @return
*/
private static int [] next(String ps){
int pLength = ps.length();
int [] next = new int [pLength];
char[]p = ps.toCharArray();
next[0] = -1;
if(ps.length() == 1){
return next;
}
next[1] = 0;
int j = 1;
int k = next[j];//看位置j的最长匹配在哪里
while(j<pLength - 1){
//现在要推出next[j+1],检查j和k位置上的关系即可
if(k<0||p[j] ==p[k]){
next[++j] = ++k;
}else{
k = next[k];
}
}
return next;
}
}