KMP
模板题链接
搞了好久可算明白了
先放个例子:
| text | a | b | c | x | a | b | c | d | a | b | x | a | b | c | d | a | b | c | d | a | b | c | y |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| num | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
| pattern | a | b | c | d | a | b | c | y | |||||||||||||||
| num | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
正常匹配算法过程:
指针 k 逐个遍历 text 字符
对于每个 k ,定义 i j,每次初始时,i = 0,j = 0
while( k + i < text.length() && j < pattern.lenth() )
{
if( text[ k + i ] == pattern[ j ] )
{
if ( j == pattern.lenth() - 1 ) res.push_back( k )
else
{
i++;j++;
}
}
}
KMP流程
按照kmp的流程:
-
两个指针,i 为 text 指针, j 为 pattern 指针
-
i 、 j 位置的字符相同,则都往前
-
当我们匹配完text与pattern的0-2后,发现两者的 3 位置不同,那就寻找 pattern 中,j - 1(即2)位往前整一串是否有相同的前后缀,没有,则 j = 0, 即从头开始重新匹配 ,此时 text[ i ] != pattern[ j ],即 text[ i ] != pattern[ 0 ], 进行 i ++
-
重复2 ,也是直到 text[ i ] != pattern[ j ]
-
此时 i = 10 , j = 6 ,寻找 pattern 中,j - 1(即5)位往前整一串是否有相同的前后缀,有 , 即“ ab ”
-
关键: 因为(1)此时 i j 不匹配, (2)前面的都匹配,(3)前面存在相同前后缀
直接把 j 移动到 前缀的后一位,继续进行 i j 的匹配。
解释如下:
因为(1) 需要重新匹配
因为(2)(3),可得: text的后缀 就是 pattern的前缀 ,
直接把 j 移动到 pattern中的前缀的 后面一位 ,就相当于完成了正常算法中从 k 开始一个个比较这里相同前后缀的 text[ i ] 和 pattern[ j ] 的过程
-
重复以上步骤,直到 j == pattern.length(), 此时完成了整串 pattern 的查找,i - j + 1 即为查找到的 pattern 在 text 中出现的位置
-
把 j = 0, i + 1,按以上步骤继续查找下一个位置
接下来是如何记录每次匹配不成功的时候 j 要退回到哪里
很简单,用上面的方法,让 pattern 自己匹配自己就行
解释如下:
上面的是在text中找pattern
那如果是在pattern中找pattern呢
是不是 每次匹配失败的位置 就是 前缀的后一位 ?
但不一样的是,即使匹配成功也要进行记录,
因为我们要的是在pattern 每个位置上匹配失败时要跳转到哪里
参考代码
#include <bits/stdc++.h>
typedef long long int LL;
using namespace std;
const int N=1e6+10,MOD=1e9+7;
string string_tobe_search,patern;
LL border[N];
vector<int> res;
void kmp_patern_self_match()
{
border[0]=0;
int j=0;
for(int i=1;i<patern.size();i++)
{
if(patern[i]==patern[j])
{
border[i]=j+1;
//i为后缀末尾,匹配失败时,匹配失败的位置就是i + 1,
//j 为 前缀末尾 , j + 1即为匹配失败后,pattern指针要跳转到的位置
j++;
}
else
{
while(j>0&&patern[i]!=patern[j]) j=border[j-1];
if(patern[i]==patern[j]) border[i]=j+1,j++;
else border[i]=0;
}
}
}
void kmp_find_patern_position()
{
int j=0;
for(int i=0;i<string_tobe_search.size();i++)
{
//如果匹配
if(string_tobe_search[i]==patern[j])
{
j++;
if(j==patern.size())
{
res.push_back(i-j+1);
//这里 +1 是因为题目要求输出位置从1开始、
//如果要求从0开始则去掉 +1
j=border[j-1];
}
}
else//如果不匹配
{
//直到 匹配 或者 j 回到 0
while(j>0&&string_tobe_search[i]!=patern[j]) j=border[j-1];
if(string_tobe_search[i]==patern[j]) j++;
}
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>string_tobe_search>>patern;
kmp_patern_self_match();
kmp_find_patern_position();
for(int pos: res) cout<<pos<<" ";
return 0;
}

浙公网安备 33010602011771号