KMP算法主要是解决包含问题,特别是在字符串方面用的比较多,主要就是看在一个字符串里面看是否包含一个字串。
要查看一个字符串里面是否包含另一个字串,大体上分为两步。1,首先拿到字串,并且拿出它的每一个字符,去算它的最长前缀和最长后缀的匹配长度,并返回next数组。2,在此基础上去比较字两个字符串。
第一步:拿子字符串去算最长前缀和最长后缀的匹配长度。
怎么理解呢?
比如说下面这个就是我们的子串。
首先拿第一个字符a,他前面没有字符了,所以我们规定它为-1。
然后拿到第二个字符串它前面自由一个字符a,我们规定他为0。
拿到第三个字符,他前面有ab,由于a和b不相等,所以为0。
来到字符串a(下标为3),它前面的串为abc,前缀a和后缀c不想等,还可以走ab和bc不相等,这时不能再走了,所以为0。
来到字符b(下标为4),它前面的字符为abca,a和a相等,此时为1,看还有没有比它大的,ab和ca不相等,再走,abc和bca不相等,不能再走了,所以为1。
此时来到字符d他前面的串为,abcab,a和b不等,在往前走一走,ab和ab一样,长度为2,再往前走看看有没有abc和cab,abca和bcab都不相等,所以为2。
所以最后返回next数组为{-1,0,0,0,1,2}。
总结:其实主要就是看前面有没有重复的。代码实现其实用的归纳法。
第二步:去比较两个字符串
假如说我们的两个字串为"abcabct"和"abcabcz",有两个指针index1,和index2,两个从0开始比较,一直来到不同位置为止,当匹配到不同的字符时就停下,然后前面第一步不是算出来了个next数组吗,这时候就用到了,利用前面的方法可以直观的看出来z位置的next数组中的值应该为3,然后index1不动,index2直接跳到3位置来接着和index1进行比较。其实从这个例子可以看出来next主要就是记录前面有没有重复串,如果有可以少比较很多。
关于计算next数组用的是数学归纳法,
比如说一个数组"abab c ababtk" 假如说我们要算k位置的长度,这时假设k前面的一个我们应该已经知道了,可以直观的看出t位置应该为4,那么这时4位置上的数和t等不等,这里可以看出来不相等的,一个是c一个是t,就直接为0。假如说是 "abab c abab a k"依然是计算k位置c和a不等,再往前分,c位置的应该是2,来到2的位置a和a相等,就直接2+1为3,所以k对应的next数组的值应为3。
代码:
public static int[] getNext(char[] str2){
int[] next = new int[str2.length];
next[0] = -1;
next[1] = 0;
int i = 2;
int cn = 0; //前半重复的的后一个
while(i < next.length){
if(str2[i -1] == str2[cn]){ //当前位置的前一个,等于前半重复的后一个,直接加一
next[i++] = ++cn;
}else if(cn > 0){
cn = next[cn]; //不相等就再往前分
}else { //到最后也没分到,就表示没有了
next[i++] = 0;
}
}
return next;
}
public static int getIndexOf(String str1,String str2){
if(str1 == null || str2 == null || str1.equals("") || str2.equals("")){
return -1;
}
char[] c1 = str1.toCharArray();
char[] c2 = str2.toCharArray();
int index1 = 0;
int index2 = 0;
int[] next = getNext(c2);
while(index1 < str1.length() && index2 < str2.length()){
if(c1[index1] == c2[index2]){ //如果字符串相等,两个指针就往后走
index1++;
index2++;
}else if(next[index2] == -1){ // 前面无重复字符串的情况
index1++;
} else{ //这种情况是前面有重复串的情况
index2 = next[index2];
}
}
return index2 == c2.length ? index1 -index2 : -1; //如果idnex2来到了跟数组长度一样,就说明找到了,否则就是没找到
}