KMP -- 字符串的匹配问题
给定一个模式串 S,以及一个模板串 P,所有字符串中只包含大小写英文字母以及阿拉伯数字。模板串 P 在模式串 S 中多次作为子串出现。求出模板串 PP 在模式串 SS 中所有出现的位置的起始下标。
暴力:只要枚举文本串的起始位置 i,然后从该位开始逐位与模式串进行匹配,如果匹配过程中每一位都相同,则匹配成功;否则,只要出现某位不同,就让文本串的起始位置变为 i + 1,并且从头开始模式串的匹配。这种做法的时间复杂度为 O(n * m)。
for (int i = 1; i <= n; i++) {
bool flag = true;
for (int j = 1; j <= m;j++) {
if(s[i+j-1] != p[j]) {
flag = false;
break;
}
}
}
假定有一个字符串 s(下标从 1 开始),那么它以 i 号位作为结尾的子串就是 s[1...i]。对该子串来说,长度为 k 的前缀和后缀分别是 s[1...k] 和 s[i-k+1...i]。现在定义一个 next 数组(说实话,这名字真奇怪QwQ),其中 next[i] 表示使子串 s[1...i] 的前缀 s[1...k] 等于后缀 s[i-k+1...i] 的最大的 k (注意:前缀跟后缀可以部分重叠,但不能是 s[0...i] 本身);如果找不到相等的前后缀,那么就令 next[i] = 0。显然,next[i] 就是所求最长相等前后缀中前缀最后一位的下标。
#include <iostream>
using namespace std;
const int N = 100010, M = 1000010;
int n, m;
int ne[N];
char s[M], p[N];
int main()
{
cin >> n >> p + 1 >> m >> s + 1;
for (int i = 2, j = 0; i <= n; i ++ )
{
while (j && p[i] != p[j + 1]) j = ne[j];
if (p[i] == p[j + 1]) j ++ ;
ne[i] = j;
}
for (int i = 1, j = 0; i <= m; i ++ )
{
while (j && s[i] != p[j + 1]) j = ne[j];
if (s[i] == p[j + 1]) j ++ ;
if (j == n)
{
printf("%d ", i - n);
j = ne[j];
}
}
return 0;
}