kmp算法可以解决什么问题?
可以解决字符串中文本串和模式串之间的匹配问题(降低时间复杂度)。
kmp数组里面的next数组是什么?前缀表是什么?
next数组是前缀表,前缀表(也就是next数组)中每个索引位置的值都是该位置的最长相同前缀后缀数,什么是前缀后缀?前缀是以第一个元素为起始不包含最后一个元素的连续串,后缀是包含最后一个元素不包含第一个元素的连续串。
为什么使用前缀表不使用其他表?
因为前缀表中记录了模式串中每个元素的最长相同前缀后缀的长度,每次在匹配文本串和模式串时遇到不相同的情况时,如果有前缀表的话可以不从头开始匹配,而是根据前缀表对应的值返回上到特定的地方进行匹配。
解题思路:
先构建一个前缀表(next数组),然后匹配模式串和文本串的时候使用next数组。我觉得难点在于题解给的构造next数组。
构造next数组的步骤:
1.进行初始化:初始双指针i,j。i初始化next数组的后缀的末尾,j初始化为next数组的前缀的末尾,j还是最长相等前缀后缀数。
2.两指针对应的字符不同的情况。
3.两指针对应字符相同的情况。
4.更新next
构建next的代码部分:
void getNext(int *next,string &a){
//不能传入数组的引用对数组进行操作,只能传入指向数组的地址对数组进行操作
int i,j=0;
next[0]=0;
//i代表后缀的末尾,j代表前缀的末尾,并且j是最大相等前缀后缀数。
for(int i=1;i<a.size();i++){
while(j>0&&a[i]!=a[j]){
j=next[j-1];//如果不相等就跳转,j的就跳转到next[j-1]
}
if(a[i]==a[j]){
j++;
}
next[i]=j;
}
}
注意点:当a[i]!=a[j]这里的判断是while而不是if,例如aabaaf,当匹配到f的时候,f的最长前缀后缀相等于就有三种可能,3和2和1,如果使用if的话,只判断了他是否是3的情况。
对于这个题解我的困惑:
这个题解,我不清楚是怎么想到这样的一个方法的,虽然我觉得这个方法很妙,但是我可以也可以用代码写出一个构建next数组的方法,只不过时间复杂度是o(n^2),所以我暂且认为写出这样的算法是因为作者的算法能力很强,在尝试过程中写出的。所以我暂时先背下来这个构造next数组的方法(怎么记呢?就是模仿匹配操作,当i,j对应字符不相同时,j=next[j-1],当相同时j++)。以后再看到再试试理解他是如何想出的。
整体题解代码:
class Solution {
public:
void getNext(int *next,string &a){
//不能传入数组的引用对数组进行操作,只能传入指向数组的地址对数组进行操作
int i,j=0;
next[0]=0;
//i代表后缀的末尾,j代表前缀的末尾,并且j是最大相等前缀后缀数。
for(int i=1;i<a.size();i++){
while(j>0&&a[i]!=a[j]){
j=next[j-1];//如果不相等就跳转,j的就跳转到next[j-1]
}
if(a[i]==a[j]){
j++;
}
next[i]=j;
}
}
int strStr(string haystack, string needle) {
if (needle.size() == 0) {
return 0;
}
int next[needle.size()];
getNext(next, needle);
//得到前缀表next,我们可以进行文本串与模板串比较的问题了
//使用两个指针i,j。i指向文本串起始位置,j指向模板串起始位置
int i,j=0;
//如果h[i]==n[j];j++.
//如果不相等j=next[j-1];然后继续进行比较
for (int i = 0; i < haystack.size(); i++) {
while(j > 0 && haystack[i] != needle[j]) {
j = next[j - 1];
}
if (haystack[i] == needle[j]) {
j++;
}
if (j == needle.size() ) {
return (i - needle.size() + 1);
}
}
return -1;
}
};