KMP总结
先来大佬的链接,很详细:
https://www.cnblogs.com/SYCstudio/p/7194315.html
https://blog.csdn.net/v_JULY_v/article/details/7041827
下面的写法是从下标0开始。
const int maxn = 1e6 + 10;
int Next[maxn];
char s[maxn], p[maxn];
//未优化的Next数组求法
//可以求多个模式串p在文本串s中的位置
//也可以求最小循环节
void getNext() {
int len_p = strlen(p);
Next[0] = -1;
int k = -1;
int j = 0;
while (j < len_p - 1) {
//p[k]表示前缀, p[j]表示后缀
if (k == -1 || p[j] == p[k]) {
++j;
++k;
Next[j] = k;
} else {
k = Next[k];
}
}
}
//优化后的Next数组求法,可以求模式串p在文本串s首次出现的位置
void GetNext() {
int len_p = strlen(p);
Next[0] = -1;
int k = -1;
int j = 0;
while (j < len_p - 1) {
//p[k]表示前缀,p[j]表示后缀
if (k == -1 || p[j] == p[k]) {
++j;
++k;
if (p[j] != p[k]) {
Next[j] = k;
} else {
Next[j] = Next[k];
}
} else {
k = Next[k];
}
}
}
//匹配
int KMP() {
int i = 0;
int j = 0;
int len_s = strlen(s);
int len_p = strlen(p);
while (i < len_s && j < len_p) {
if (j == -1 || s[i] == p[j]) {
++i;
++j;
} else {
j = Next[j];
}
}
if (j == len_p) {
return (i - j); //返回首字母在s中的位置下标
} else {
return (-1);
}
}
//主函数
int main() {
scanf("%s%s", s, p);
GetNext();
printf("%d\n", KMP() + 1);
return 0;
}
/**
* https://www.cnblogs.com/SYCstudio/p/7194315.html
* KMP其他写法
*/
//求解F数组:
for (int i = 1; i < m; i++) {
int j = F[i - 1];
while ((B[j + 1] != B[i]) && (j >= 0))
j = F[j];
if (B[j + 1] == B[i])
F[i] = j + 1;
else
F[i] = -1;
}
//利用F数组寻找匹配,这里我们是每找到一个匹配就输出其开始的位置:
while (i < n) {
if (A[i] == B[j]) {
i++;
j++;
if (j == m) {
printf("%d\n", i - m + 1);//注意,这里输出的位置是从1开始标号的,如果你要输出从0开始标号的位置,应该是是i-m.
j = F[j - 1] + 1;
}
} else {
if (j == 0)
i++;
else
j = F[j - 1] + 1;
}
}
//另一个写法,不过这里字符串是从 1 开始标号的,如果上面理解了的话不难转化。
Nxt[0] = Nxt[1] = 0;
for (int i = 2, j = 0; i <= m; i++) {//构建 Next
while (j && T[j + 1] != T[i]) j = Nxt[j];
if (T[j + 1] == T[i]) ++j;
Nxt[i] = j;
}
for (int i = 1, j = 0; i <= n; i++) {//匹配
while (j && T[j + 1] != S[i]) j = Nxt[j];
if (T[j + 1] == S[i]) ++j;
if (j == m) Mch[i] = 1, j = Nxt[j];//匹配成功
}