KMP
28.找出字符串中第一个匹配项的下标
haystack称为文本串,needle称为模式串
构建next数组的操作
对于例子
当出现字符串不匹配时,可以知道一部分之前已经匹配的文本内容,可以利用这些信息避免从头再去做匹配了。
通过next数组(前缀表)我们可以发现,当模式串里的f和文本串的b不相同时,应该将模式串中的指针j回退到索引为2,即j=next[j-1]
可以看到前缀表中表示以当前字符为结尾的串的相同前缀和后缀长度,如next[1],表示aa这个串的最长相同前缀和后缀的长度为1:就是a.
那么当f不匹配(s[i]!=t[j])
的时候,我们已知之前一定是都和文本串匹配上了,才会来到这一步的判定,所以我们只需要获取next[j-1],就可以省略掉模式串里前缀的匹配(因为当前的文本串里的后缀已经和模式串前缀相同了,图中就是,因为next[j-1]=2,所以aa已经匹配过了,只需要比较模式串里的’b‘和文本串里的’b’即可。
对于生成next数组,是在模式串本身的基础上生成的,i指针表示后缀的末尾单词,j指针表示前缀的末尾单词
后面则是进行文本串和模式串的匹配,其基本原理都是:
在当前字符不相同的时候,则因为之前匹配文本串的后缀和模式串中的前缀相同,所以可以省略这部分的判断,继续判断下一个字符,而使用while进行j的回退则是因为,可能前缀和后缀是相同了,但是下一个字符开始就不相同了,所以继续j回退到一个新的前缀和后缀相同的状态,再看两个串的下一个字符是否相同。
class Solution {
public:
void getNext(int* next,string& s){
int j = 0;
next[0] = j;
// i遍历后缀末尾位置
// j指向前缀末尾位置
for(int i = 1; i < s.size(); i++){
// 如果下一个字母和当前i指向的不相同,那么就让j回到之前前缀和
// 后缀已经匹配的位置,然后进行下一次比较
while(j > 0 && s[i] != s[j]){
j = next[j - 1];
}
// 如果下一个字符和i对上了,代表前缀后缀匹配的长度增加了
if(s[i] == s[j]) j++;
// 在当前位置i,如果i+1的字符没匹配上,则可以回退到next[i],代表在i位置已经匹配上的前缀或后缀长度
next[i] = j;
}
}
int strStr(string haystack, string needle) {
string s = haystack;
string t = needle;
if(t.size() == 0) return 0;
vector<int> next(needle.size());
getNext(&next[0], t);
int j = 0;
for(int i = 0; i<s.size(); i++){
while(j > 0 && s[i] != t[j]){
j = next[j - 1];
}
if(s[i] == t[j]) j++;
if(j == t.size()) return i -(t.size() - 1);
}
return -1;
}
};
459.重复的子字符串
后面的扩展题就没写了,因为很难,等二刷再补