package interview;
/**
* create by liuzhiwei on 2020/4/6
* 字符串匹配 indexof 返回字串第一次出现的位置
*/
public class HuaWei02_KMP {
/**
* BF算法:是一种蛮力算法。
* 将目标串S的第一个字符与模式串T的第一个字符进行匹配,若相等,则继续比较S的第二个字符和 T的第二个字符;
* 若不相等,则比较S的第二个字符和T的第一个字符,依次比较下去,直到得出最后的匹配结果。
* <p>
* time:O(MN) M 为主串的长度 N为子串的长度
* <p>
* 以上算法完全可以实现要求的功能 ,而且在字符重复概率不大的情况下,时间复杂度也不是很大,一般为O(M+N)。
* 但是一旦出现如下情况,时间复杂度便会很高,如:子串为“111111110”,而主串为 “111111111111111111111111110” ,
* 由于子串的前8个字符全部为‘1’,而主串的的前面一大堆字符也都为1,这样每轮比较都在子串的最后一个字符处出现不等,
* 因此每轮比较都是在子串的最后一个字符进行匹配前回溯,这种情况便是此算法比较时出现的最坏情况。
* 因此该算法在最坏情况下的时间复杂度为O(M*N),另外该算法的空间复杂度为O(1)
* <p>
* KMP算法的主要思想:
* 上述算法的时间复杂度之所以大,是由于索引指针的回溯引起的,针对以上不足,便有了KMP算法
* KMP算法可以在O(Plen+Tlen)的时间数量级上完成串的模式匹配操作。
* 其改进之处在于:每一趟比较重出现字符不等时,不需要回溯索引指针i,
* 而是利用已经得到的部分匹配的结果将子串向右滑动尽可能远的距离,继续进行比较。
* 它的时间复杂度为O(M+N),空间复杂度为O(N),这从后面的代码中可以看出
*
* @param str
* @param sub
* @return
*/
public static int find(String str, String sub) {
int lengthA = str.length();
int lengthB = sub.length();
int i = 0;
int j = 0;
//前缀数组
int[] next = getNext(sub);
while (i < lengthA && j < lengthB) {
//如果当前字符相同,则继续向下比较 // 继续比较 必须j==-1在前面 0位置前面没有字符,人为规定 -1
// 如果 j = -1 说明j回到开头,都没有匹配上 i++从下一个开始 j++ 从0开始匹配
if (j == -1 || str.charAt(i) == sub.charAt(j)) {
i++;
j++;
} else {
// 如果采用简单的BF算法,则每趟比较i都要回退,而采用KMP算法,每趟比较时,i保持不变,只需将j向右滑动即可,也即是减少了中间一些趟次的比较。
//j后退重新匹配,i不变
j = next[j];
}
}
//如果j到末尾了说明匹配成功了
if (j == lengthB) {
return i - j;
} else {
return -1;
}
}
private static int[] getNext(String sub) {
if (sub.length() == 1) {
return new int[]{-1};
}
int[] next = new int[sub.length()];
//前两个元素确定的
next[0] = -1;
next[1] = 0;
//从2开始
int i = 2;
//记录每次比较的最小前缀 从0开始 如果前一个字符和后一个字符相等
int cn = 0;
//这一段比较难理解
while (i < sub.length()) {
if (sub.charAt(i - 1) == sub.charAt(cn)) {
next[i++] = ++cn;
//新来的字符和cn的对应的字符不相同,则cn从头开始匹配 如果新来的字符和cn的对应的字符相同 则cn++
} else if (cn > 0) {
cn = next[cn];
} else {
next[i++] = 0;
}
}
return next;
}
public static void main(String[] args) {
int index = find("badsdsdbc", "a");
System.out.println(index);
}
}
KMP算法
最新推荐文章于 2021-09-10 11:11:24 发布