Day09_字符串
KMP来啦
找出字符串中第一个匹配项的下标
下标5之前这部分的字符串(也就是字符串aabaa)的最长相等的前缀 和 后缀字符串是 子字符串aa ,因为找到了最长相等的前缀和后缀,匹配失败的位置是后缀子串的后面,那么我们找到与其相同的前缀的后面从新匹配就可以了。
所以前缀表具有告诉我们当前位置匹配失败,跳到之前已经匹配过的地方的能力。
KMP写法一:next数组第一个记录为0,相当于记录了前缀表
class Solution {
public:
void getNext(string &needle, int *next) {
int j = 0;
int needle_len = needle.length();
next[0] = 0;
for (int i = 1; i < needle_len; ++i) {
while (j > 0 && needle[i] != needle[j]) { // 如果没匹配上,就一直往前找
j = next[j - 1]; // 当前这一位没有匹配上,应该回退到上一位记录的相等前缀的后一位
}
if (needle[i] == needle[j]) { // 如果匹配上了,就将相等前后缀的长度+1
j++;
}
next[i] = j;
}
}
int strStr(string haystack, string needle) {
int haystack_len = haystack.length();
int needle_len = needle.length();
int next[needle_len];
getNext(needle, next);
int j = 0;
for (int i = 0; i < haystack_len; ++i) {
while (j > 0 && haystack[i] != needle[j]) { // 如果没匹配上,就一直往前找能匹配上的前缀
j = next[j - 1]; // 当前这一位没有匹配上,应该回退到上一位记录的相等前缀的后一位
}
if (haystack[i] == needle[j]) { // 如果匹配上了,就继续向后匹配
j++;
}
if (j == needle_len) { // 返回匹配到的模式串在文本串中出现的第一个位置
return (i - j + 1);
}
}
return -1;
}
};
KMP写法二:next数组第一个记录为-1,即前缀表统一减一实现。
直接得到next表示前缀表的形式,匹配不上的时候要去前一个点找前缀位置
前缀表统一减一后,匹配不上的位置next中对应的就是应该去找的前缀的位置
class Solution {
public:
void getNext(string &needle, int *next) {
int j = -1;
int needle_len = needle.length();
next[0] = j;
for (int i = 1; i < needle_len; i++) {
while (j >= 0 && needle[i] != needle[j + 1]) { // 由于j从-1开始,所以j+1才能对应到数组的下标上
j = next[j];
}
if (needle[i] == needle[j + 1]) {
j++;
}
next[i] = j;
}
}
int strStr(string haystack, string needle) {
int haystack_len = haystack.length();
int needle_len = needle.length();
int next[needle_len];
getNext(needle, next);
int j = -1;
for (int i = 0; i < haystack_len; ++i) {
while (j >= 0 && haystack[i] != needle[j + 1]) {
j = next[j];
}
if (haystack[i] == needle[j + 1]) {
j++;
}
if (j + 1 == needle_len) {
return (i - needle_len + 1); // 注意此时j+1才等于needle_len注意返回制别错了
}
}
return -1;
}
};