引言:今天被这道题整笑了,也被这道题打醒了,原来我还没有真正的理解KMP算法
先来讲讲这道题有多有趣先:
一:
对于熟悉了解面向对象的封装性来说,解决这道题只需要一行代码哈哈哈有被笑到,以下是Java代码:
return haystack.indexOf(needle);
OK今天的面试到此结束,可以回去等通知了
二.
暴力枚举:每一个都进行比较
class Solution { public int strStr(String haystack, String needle) { if(needle.equals("")) return 0; int slow, fast, m = haystack.length(), n = needle.length(), k; for(slow = 0; slow < m - n + 1; slow++){ k = slow; for(fast = 0; fast < n; fast++){ if(haystack.charAt(k) != needle.charAt(fast)) break; k++; } if(fast == n) return slow; } return -1; } }
虽然都可以过,但性能太差,所以下面主角登场---------KMP算法,专治各种子串匹配:
说来惭愧,在使用它前我又忘了怎么算,于是又去翻了资料,于是出于胜负欲的刺激下,我这次要完完整整踏踏实实深深刻刻的干了KMP算法,并且做好笔记,理解每一处细节
1.首先,获取next[] 数组,下面我直接上图吧,扣字描述费键盘
需要的话就多手写几个例子,手写不难,主要是代码是理解较费劲
然后直接上代码:
public static void getNext(int[] next, String sonStr){ int len = sonStr.length(); char[] ch = sonStr.toCharArray(); int i = 0, k = 1;/*求从第三个起的前后缀相同字符个数, //i表示前缀开始,k表示后缀开始(即每次的j-1) 因此第j个的表示是:k+1 */ next[0] = -1; next[1] = 0; while(k < len - 1 ){ if(i != -1 && ch[i] == ch[k]){ next[k+1] = i + 1;/* 这里就是第j个的赋值, 因为i从0开始,所以计算个数要+1 */ i++; k++; } else if(i == -1){ next[k + 1] = 0; /*如果i == -1,就是说明第j个的前后缀没有相同的字符,就赋值0然后继续比较下一个,所以k++ 那么前缀继续由第一个开始,即下标0,所以i++ */ i++; k++; } else{ //这里是重点,i = next[i],我个人认为有动态规划的嫌疑 //因为此处前后缀不匹配,但也许前缀的i倒退几格就能与后缀匹配上了 //因此要借助前面的值才知道我们的i要推回哪里,回到-1就说明匹配不上,因此有动态规划的嫌疑 i = next[i]; } } }
详解可看上面代码的讲解
最后附上完整调试代码:
package FirstPackage; public class KMP { public static void main(String[] args) { String fatherStr = "mississippi"; String sonStr = "issip"; System.out.println(testKMP(fatherStr,sonStr)); } public static int testKMP(String fatherStr,String sonStr) { int n = fatherStr.length(), m = sonStr.length(); if(m == 0) return 0; else if(m < 2) { return fatherStr.indexOf(sonStr.charAt(0)); } else { int[] next = new int[m]; getNext(next, sonStr); int i = 0, j = 0; //while循环一定要这么写,俩者一个不成立都不行,否则会报下标越界的错误 while( j < n && i < m ) { if(i == -1 || fatherStr.charAt(j) == sonStr.charAt(i)) { i++; j++; } else i = next[i]; //不匹配 i 就回到i该回去的地方 } if(i == m) return j - i; //举例手算一下,就是这个值 else return -1; } } public static void getNext(int[] next, String sonStr){ int len = sonStr.length(); char[] ch = sonStr.toCharArray(); int i = 0, k = 1; next[0] = -1; next[1] = 0; while(k < len - 1 ){ if(i != -1 && ch[i] == ch[k]){ next[k+1] = i + 1; i++; k++; } else if(i == -1){ next[k + 1] = 0; i++; k++; } else{ i = next[i]; } } } }
然后我们再提交一下结果,看看KMP的性能:
测试代码如下:–这里所有可能情况都一一考虑到了
class Solution { public int strStr(String haystack, String needle) { int n = haystack.length(), m = needle.length(); if(m == 0) return 0; else if(m < 2) { return haystack.indexOf(needle.charAt(0)); } else { int[] next = new int[m]; getNext(next, needle); int i = 0, j = 0; while( j < n && i < m ) { if(i == -1 || haystack.charAt(j) == needle.charAt(i)) { i++; j++; } else i = next[i]; } if(i == m) return j - i; else return -1; } } public static void getNext(int[] next, String sonStr){ int len = sonStr.length(); char[] ch = sonStr.toCharArray(); int i = 0, k = 1; next[0] = -1; next[1] = 0; while(k < len - 1 ){ if(i != -1 && ch[i] == ch[k]){ next[k+1] = i + 1; i++; k++; } else if(i == -1){ next[k + 1] = 0; i++; k++; } else{ i = next[i]; } } } }
哇giao,大佬们研究的算法就是不一样。而对于我这种菜鸟,只需要站在巨人的肩膀上,记住和学习他们的思想就行了
好了,文章到这里终于结束,希望这次能真正地悟道巨人们的思想,资料不再翻来翻去