本小白今天学习了KMP算法(太疲劳了,尽管脑壳疼也要把今天的学习任务记录下来应付秋招~ ),KMP算法名字的由来据说是三个人名,解决的是在字符串str1中匹配str2的问题。如果str1的某一子串为str2,则返回str2字符串的第一个字符在str1中角标,否则返回-1。
暴力解
这个问题简单粗暴的解法的就是从str1的第一个字符开始比对str2,若出现不符合的情况,则由str1下一个字符开始重新匹配,直至str1遍历结束(失败)或str2匹配成功(成功)。如果str1的长度为n,str2的长度为m,则时间复杂度为O(n * m)。这个方法在笔试时必定超时。
贴代码:
public static int solution(String str1, String str2) {
if (str1.length() == 0 || str2.length() == 0) return -1;
for (int i = 0; i < str1.length(); i++) {
int j = 0;
for (; j < str2.length(); j++) {
if (i + j >= str1.length() || str1.charAt(i + j) != str2.charAt(j)) {
break;
}
}
if (j == str2.length()) {
return i;
}
}
return -1;
}
KMP算法
这里就不分析原理了(实在懒于画图 ),具体的流程和原理在网上各位大佬的博文里都能找到。这里大致提一下流程,KMP算法中用到了一个辅助数组,个人认为也是空间换时间的经典案例了,KMP算法大致分为两步:
- 先求解辅助数组next[]数组;
- 再进行比对。
其中,next[]数组的长度和str2数组的长度一致,它的每个元素next[i]表示在str2[0]~str2[i - 1]中最大的 前缀字母和后缀字母相同的个数并且这个长度必须要小于i(这个一会儿解释),next[0]固定设置为-1。
举个栗子,str1 = “abcbcabc”,str2 = “bcbcb”;
next[0]:0这个固定设置为-1;
next[1]:“b”(str2[0 至 i] = “b”)的 最大前缀字母和后缀字母相同的个数并且这个长度必须要小于i,那么就是0了,因为"b"的第一个前缀为b,第一个后缀也为b,他们相同,长度为1,但要小于i(此时i = 1),所示只能为0;(为什么长度要小于i,因为str2[0]~str2[i - 1],它本身必然等于它本身,这没什么好记录的)
next[2]:"bc"的第一个前缀为b,第一个后缀为c,不相同,所以next[2] = 0;
next[3]:"bcb"的第一个前缀为b,第一个后缀为b,相同;前缀为2时为“bc”,后缀为2时为“cb”,也不相同,所以next[3] = 1;
next[4]:“bcbc”,next[4] = 2;
综上:next[] = {-1,0,0,1,2};
讲解完next数组的含义,开始讲解究竟如何实现创建next[]的过程。next[0] = -1; next[1] = 0, 这两种情况就不讲解了。我们看任意位置next[i]如何求解:
- 再此之前,先定义一个k(跳转位置);
- 如果str2[i - 1]的字符与跳转位置的字符str2[k]相同,则next[i] = k + 1;i ++;k ++;
- 如果str2[i - 1]的字符与str2[k]不相同,那么先令k = str2[k],继续比较str2[i - 1]与str2[k]是否相同,重复2、3步骤,直到它们相同或者k = -1。
贴代码:
public static int[] getNext(String s) {
int[] next = new int[s.length()];
if (s.length() == 0) return next;
next[0] = -1;
if (s.length() == 1) return next;
int k = -1;//跳转位置
int j = 1;//计算位置
while (j < s.length()) {
if (k == -1 || s.charAt(j - 1) == s.charAt(k)) {
next[j++] = ++k;
} else {
k = next[k];
}
}
return next;
}
求完next[]数组后就是比对的过程了。str1与str2比对的流程:
- 若str1[i] == str2[j],则i ++,j++;
- 若str1[i] != str2[j],则令j = next[j],str1[i]继续和str2[j]比对,若直到j = 0 即next[j] = -1时str1[i]和str2[j]还不相同,i++;
- 重复1,2的过程直至j = str2.length()(成功),或者j != str2.length() && i = str1.length()(失败)。
贴代码:
public static int KMP(String s1, String s2) {
if (s1.length() <= 0 || s2.length() <= 0 || s1.length() < s2.length()) return -1;
int index1 = 0;
int index2 = 0;
int[] next = getNext(s2);
while (index2 < s2.length() && index1 < s1.length()) {
if (index2 == -1 || s1.charAt(index1) == s2.charAt(index2)) {
index1++;
index2++;
} else {
index2 = next[index2];
}
}
return index2 == s2.length() ? index1 - index2 : -1;
}
到此KMP算法结束了,写的比较简洁,没有贴图示意,主要还是为了记录本小白新学习的内容加强记忆~