从求子串的算法题开始
实现 strStr() 函数。
给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始)。如果不存在,则返回 -1。
示例 1:
输入: haystack = "hello", needle = "ll"
输出: 2
示例 2:
输入: haystack = "aaaaa", needle = "bba"
输出: -1
说明:
当 needle 是空字符串时,我们应当返回什么值呢?这是一个在面试中很好的问题。
对于本题而言,当 needle 是空字符串时我们应当返回 0 。这与C语言的 strstr() 以及 Java的 indexOf() 定义相符。
对于这个算法题,第一反应就是把needle串一位位依次与haystack串进行比较,我们称之为朴素算法,这样的做法明显有很多的重复,例如:
这时聪明人应该这么做:
(图片取自LeetCode题解)
KMP算法
KMP算法是由三位大神联合发明的,不用我说你们应该也能想到KMP就分别是这三位大佬的名字首字母,那么他究竟是如何实现上述的聪明算法的呢?
这里我主要参考的大话数据结构这本书,大家如果刚开始刷算法题,也建议看完这本书补补基础再开刷,事半功倍哦!
如图是我所说的朴素算法的过程,我们可以看到,这里面2~5步都是多余的,这是因为:假设我们已经知道匹配串不重复(这是前提,之后会说有重复怎么办),如上图的abcdex,那么第一步之后判断出前5个都对应,而第6个不对应后,不论被匹配串的第7位是什么,第②步都不可能全部对应上,因为前5个全都不对应。同理可得2到5步全都不需要,直接判断第6步即可。
那么KMP算法是如何做到这么聪明的呢,该算法使用了两个指针,分别指向模板串和匹配串。
模板串的指针不会回退,只能一步步向前加。
匹配串的指针可以回退,而怎么进行回退是通过一个next数组来做的,next数组长度与匹配串长度相等,其中每一位的值代表如果到该位字符串不相等了,那么下一步跳到匹配串的哪一位继续。
例如上图中第一步时到了匹配串的第6位不相等了,那么i应该不变,而j应该跳到匹配串的第一位,所以next数组的第六位就应该是0。下图是大话数据结构里的例子,但是要注意他的数组下标有一些问题,理解这个意思就行了。
注意书上的j是从1开始的,但我们实际编程时数组第一位下标为0.
理解了next数组之后我们来看一下代码实现。
vector<int> getnext(string P){
vector<int> next(P.size());
int q,k;//q:模版字符串下标;k:最大前后缀长度
int m = P.size();//模版字符串长度
next[0] = 0;//模版字符串的第一个字符的最大前后缀长度为0
for (q = 1,k = 0; q < m; ++q)//for循环,从第二个字符开始,依次计算每一个字符对应的next值
{
while(k > 0 && P[q] != P[k])//递归的求出P[0]···P[q]的最大的相同的前后缀长度k
k = next[k-1]; //不理解没关系看下面的分析,这个while循环是整段代码的精髓所在,确实不好理解
if (P[q] == P[k])//如果相等,那么最大相同前后缀长度加1
{
k++;
}
next[q] = k;
}
return next;
}
我们可以看到,如果匹配串全部相等,那么k的值会一直增加,这很好理解,这里面最难理解的就是while(k > 0 && P[q] != P[k]) k = next[k-1];
这一句。这就是之前说的匹配串中如果有重复该怎么办。
以上面例子中的“abcabx”为例,前四位不等的时候next数组为“0000”,到了第4位时和开头a相等了,那么next数组接下来两位就是“12”,这是因为再比较时,前两位ab就不用再比了,因为已经相等了,如果再往后加一位
class Solution {
public:
int strStr(string haystack, string needle) {
if(needle.empty())
return 0;
vector<int> next = getnext(needle);
int n,m;
int i,q;
n = haystack.size();
m = needle.size();
for (i = 0,q = 0; i < n; ++i)
{
while(q > 0 && needle[q] != haystack[i])
q = next[q-1];
if (needle[q] == haystack[i])
{
q++;
}
if (q == m)
{
return i-q+1;
}
}
return -1;
}
};
KMP算法的优化
例如匹配串为“aaaaab”,模板串前4位为“aaac”,那么第4位检测到不等时,应该将匹配串跳到底。之后补充