本题是经典的KMP算法。我们先来看看字符串匹配的暴力解法,假设字符串S[N]
和匹配串p[M]
。
bool flag = false;
for(int i = 0; i < N; ++i) {
flag = true;
for(int j = 0; j < M; ++j) {
if(p[j] != s[j + i]) {
flag = false;
break;
}
}
}
如下图所示,暴力解法的整体思路大概是,当P和S某个位置的字符不一致时,P就整体往前进一位,然后在进行匹配。这样整体的时间复杂度是O(N * M)。
但是,可以观察到已经有一部分字符已经匹配成功,我们是不是可以利用这部分成功匹配的信息来使得P可以尽可能往前进,减少匹配次数。实际上,如下图所示。
我们只需要获知字符串前缀与后缀相等的最大长度即可。
如图所示,如果P只有第一位和最后一位相等,即可以直接将P放置到第三行的位置。
所以,我们需要对p进行处理,获取到每个位置最长前缀字符串的末位置的坐标,这样我们就可以借助这个信息来帮助我们移动P了。
首先,我们先来看一下匹配过程。假设我们已经有了next数组了。
#include <iostream>
using namespace std;
const int N = 100010, M = 1000010;
char s[M], p[N];
int n, m;
int ne[N]; // next数组
int main() {
cin >> n >> p + 1 >> m >> s + 1;
for(int i = 1, j = 0; i <= m; ++i)
{
while(j && p[j + 1] != s[i]) j = ne[j];
if(p[j + 1] == s[i])
{
j++;
}
if(j == n)
{
cout << i - n << " ";
j = ne[j];
}
}
}
接下来,next数组求法与上述相似。
#include <iostream>
using namespace std;
const int N = 100010, M = 1000010;
char s[M], p[N];
int n, m;
int ne[N]; // next数组
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 && p[j + 1] != s[i]) j = ne[j];
if(p[j + 1] == s[i])
{
j++;
}
if(j == n)
{
cout << i - n << " ";
j = ne[j];
}
}
}