- 2022tysc0250 的博客
KMP
- 2024-8-7 18:45:36 @
KMP
一些基础定义
- 表示前缀; 表示后缀;
asdfas
中asdf
是该串的一个周期, 一定是 的周期; 若周期 满足 则称 为之的循环节;-
性质:
是 的周期 是 的
不具有单调性
-
Border
定义
若字符串 的同长度的前缀和后缀完全相同,即 时,则称此前缀或后缀为一个 (根据语境,有时 也指长度),字符串本身也是它的 (根据语境有时候又不是了)。
题目:若 ,求出所有 。
性质
- 传递性: 的 的 也是 的 。
- 求 的所有 等价于求 所有前缀的最大 。
KMP
的非平凡的最大 (不包含自身),。
考虑 的所有长度大于 的 ,去掉最后一个字母,就会变成 的 。
因此求 的时候,可以遍历 的所有 ,即 ,检查最后一个字符是否等于 。
for(int i = 2;i <= n + 1;i++)
{
int j = next[i - 1];
while(s[i] != s[j + 1] && j) j = next[j];
if(s[i] == s[j + 1]) j++;
next[i] = j;
}
字符串匹配
给出两个字符串 和 ,求 在 中出现次数和所有出现的位置。
例: abababc
, aba
,则所有出现位置为 和 。
- Hash:枚举起始位置,然后用 Hash 检查。复杂度 ,常数较大,具有一定的不确定性。
- KMP:
- 充分利用前缀匹配的有效信息,即 数组( 的性质),进行快速转移。复杂度 。
- 代码:
#include <bits/stdc++.h>
using namespace std;
int next[1000001],n,m,ans;
string s,t;
signed main()
{
cin >> s >> t;
n = s.size(),m = t.size();
s = "." + s;
t = "." + t;
for(int i = 2;i <= n + 1;i++)
{
int j = next[i - 1];
while(s[i] != s[j + 1] && j) j = next[j];
if(s[i] == s[j + 1]) j++;
next[i] = j;
}
for(int i = 1,j = 0;i <= m;i++)
{
while(t[i] != s[j + 1] && j) j = next[j];
if(t[i] == s[j + 1]) j++;
if(j == n){ans++;j = next[j];}
}
printf("%d",ans);
return 0;
}