我相信不少人都据说过KMP算法(PS:在上数据结构的时候,这个算法自始至终都没想明白)java
你们也知道KMP算法是用来寻找目标子串的算法,可是都没有真正搞懂KMP。以前,我也是如此,我疑惑的有:程序员
但愿这篇文章能对大家有所帮助!算法
KMP这种算法,单单靠看文章,仍是很难弄懂的,结合图像,以及视频的帮助会更容易弄懂。这里我推荐一个视频:KMP算法数组
我的也是经过这个视频,恍然大悟的。这里仍是很是感谢这位做者的分享。数据结构
经过上述视频,我想你们应该都有一个大概的了解了。该算法我将它划分为两个部分:ide
我将分这两个部分进行细致讲解函数
求Next数组是KMP算法的重点,也是难点!想搞懂这部分必定要看视频!必定要看视频!必定要看视频!我以为上述的视频,将这部分说的很清楚了。这里作一个总结:
(视频里将目标子串的第i
号位置映射到Next指针的第i+1
号位置。我这里根据程序员的习惯,将目标子串的第i
号位置映射到Next指针的第i
号位置)优化
我将Next[i]作一下定义:指针
Next[i]表示:code
- 若是(substr.charAt(i) != str.charAt(j)),这时候,将i = Next[i],再进行字符串匹配。
这种表达虽然不严谨,可是我感受比较直观hhhh。也即:若是目标子串第i个的值与匹配串第j的值不一致的时候,将目标子串向右滑动,滑动到i = Next[i]。
Next[i]值的定义:
Next[i]的值为:
- 第0位为头的前缀s1,与第i-1为尾的后缀s2,寻找他们的最长相同先后缀。
- 最长相同先后缀:就是s1与s2的值是相同的,可是s1和s2的长度都应小于i-1.
上面这两个定义值得细品,这也是KMP的精髓,也正是Next数组,KMP算法才能更加高效!
其中须要注意的点是:Next[0] = 0,Next[1] = 0,由于从第2个开始,s1和s2才有意义。
看完上述定义,我将求Next数组的函数实现出来。
注:该函数求Next数组时间复杂度较高,便于理解,可是实用性不高。优化策略做者采用DP,请看后一篇博客。
/** * 该函数是为了,根据目标子串subStr,求解该子串的Next数 * @param 目标子串substr * @return subStr的Next数组 */ static int[] CalculateNext(String substr){ //init int[] Next = new int[substr.length()]; //求解Next[i] for(int i = 2; i < substr.length(); i++){ //第0位为头的l前缀,与第i-1为尾的r后缀 int left = 0; int right = i - 1; String l = Character.toString(substr.charAt(left)); String r = Character.toString(substr.charAt(right)); int maxLen = 0; //当l与r均小于i-1的时候,扩大搜索最长相同先后缀 while(left < i - 1){ //若是两个字符串相同,说明这是 目前 最长的相同先后缀 if(l.equals(r)) maxLen = l.length(); left++; right--; //继续扩大搜索范围 l = l + Character.toString(substr.charAt(left)); r = Character.toString(substr.charAt(right)) + r; } //最终的maxLen即为Next[i]的值 Next[i] = maxLen; } return Next; }
这个部分就简单不少了,只要注意一点小细节,总体思路很是清晰直观:
在保证目标子串与主串匹配的过程当中,不会越界的状况下:进行目标子串的滑动操做
当subStr.charAt(i) == str.charAt(j)
的时候,i与j同时往右移动便可,当彻底匹配的时候,就返回
当subStr.charAt(i) != str.charAt(j)
的时候,i = Next[i]便可。
这里j须要注意一个细节,若是i == 0,须要j往右移一位,由于和主串的第一位都不匹配,前面就再也没有能匹配的串了。
/** * 该函数用于查看目标子串在主串中第一次出现的位置 * @param 目标子串subStr * @param 主串str * @param Next数组 * @return str中,第一次出现subStr的位置 */ static int findPosition(String subStr,String str,int[] Next){ //初始化两个字符串的指针 int i = 0;//i为目标子串的指针 int j = 0;//j为主串的指针 //保证目标子串与主串匹配的过程当中,不会越界的状况下: while(j + subStr.length() - i <= str.length()){ //当两者匹配的时候,i与j同时往右移 if(subStr.charAt(i) == str.charAt(j)){ i++;j++; //当彻底匹配的时候,返回 if(i == subStr.length()) return j - subStr.length(); } //不匹配的时候,主串指针j只有在i = 0的时候,才右移动。 else { if(i == 0) j++; //i值都须要更新 i = Next[i]; } } return -1; }
这里主要探讨的是Next数组,这个是KMP算法的核心!之前考试都是考Next数组的求解,可见Next数组的重要性。