KMP字符串
KMP
已知字符串 \(l1\) 和 模式串 \(l2\)。
KMP的主要做法为:建立一个\(next\)数组,记录与以\(l2[0]\) 开头, \(l2[i]\)结尾的一段字符串的最长与后缀相同的前缀最后一个字符的下标。
举个栗子
参考代码
void getne(string s2, int l2)
{
ne[0] = -1;
int p1 = 1, p2 = -1; // p1, p2 分别是 两个指针
for (; p1 < l2; p1++)
{
while(p2 >= 0 && s2[p2 + 1] != s2[p1]) // 不同就跳到与此后缀相同的前缀
p2 = ne[p2];
if(s2[p2 + 1] == s2[p1]) // 相同就向前
p2++;
ne[p1] = p2; // 记录 ne
}
}
建好\(next\)数组后,与字符串\(l1\)匹配, 用两个指针\(p1, p2\)遍历字符串, \(l1[p1], l2[p2 + 1]\)不匹配则缩短长度;
匹配则两个指针同时向前移,当长度为\(l2\)的长度时, 用ans数组存储答案。
举个栗子
参考代码
void kmp(string s1, string s2)
{
int l1 = s1.length(), l2 = s2.length(); // l1, l2 分别是 s1 和 s2 的长度
int p1 = 0, p2 = -1; // p1, p2 分别是 两个指针
getne(s2, l2); // 拿到 ne(next简称) 数组
for (; p1 < l1; p1++)
{
while (p2 != -1 && s1[p1] != s2[p2 + 1]) // 不同就跳到与此后缀相同的前缀
p2 = ne[p2];
if (s1[p1] == s2[p2 + 1]) // 相同就向前
p2++;
if (p2 == l2 - 1) // 记录答案
{
ans[++pos] = p1 - p2;
p2 = ne[p2];
}
}
}
例题
完整代码
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
int ne[N], pos = 0, ans[N];
void getne(string s2, int l2)
{
ne[0] = -1;
int p1 = 1, p2 = -1; // p1, p2 分别是 两个指针
for (; p1 < l2; p1++)
{
while(p2 >= 0 && s2[p2 + 1] != s2[p1]) // 不同就跳到与此后缀相同的前缀
p2 = ne[p2];
if(s2[p2 + 1] == s2[p1]) // 相同就向前
p2++;
ne[p1] = p2; // 记录 ne
}
}
void kmp(string s1, string s2)
{
int l1 = s1.length(), l2 = s2.length(); // l1, l2 分别是 s1 和 s2 的长度
int p1 = 0, p2 = -1; // p1, p2 分别是 两个指针
getne(s2, l2); // 拿到 ne(next简称) 数组
for (; p1 < l1; p1++)
{
while (p2 != -1 && s1[p1] != s2[p2 + 1]) // 不同就跳到与此后缀相同的前缀
p2 = ne[p2];
if (s1[p1] == s2[p2 + 1]) // 相同就向前
p2++;
if (p2 == l2 - 1) // 记录答案
{
ans[++pos] = p1 - p2;
p2 = ne[p2];
}
}
}
int main()
{
int t;
string a = "", b = "";
cin >> t;
for(int i = 1; i <= t; i++)
{
char c;
cin >> c;
a += c;
}
cin >> t;
for(int i = 1; i <= t; i++)
{
char c;
cin >> c;
b += c;
}
kmp(b, a);
for(int i = 1; i <= pos; i++)
{
cout << ans[i] << ' ';
}
return 0;
}
实在不理解的话, 看这里

浙公网安备 33010602011771号