KMP模板
Posted iuk11
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了KMP模板相关的知识,希望对你有一定的参考价值。
先导概念
s[ ]
模式串
p[ ]
匹配串
非平凡前缀
指除了最后一个字符以外,一个字符串的全部头部组合(前面连续的部分)
非平凡后缀
指除了第一个字符以外,一个字符串的全部尾部组合
部分匹配值
非平凡前缀和非平凡后缀的最大相似长度
next[ ]
第i个元素的,前i个元素的字符串,的部分匹配值。
与模式串匹配
若在下标i处匹配失败,若模式串与匹配串有一个以s[i]结尾的相似子串,那个就可以跳过这段匹配,把匹配串直接移动到下一个需要匹配的位置(匹配串的当前下标移动到len+1处,len为相似子串的长度)。
求next数组
for(int i=2,j=0;i<=n;i++)//更新匹配串的next[]数组
while(j&&p[i]!=p[j+1]) j=ne[j];//当前不匹配前面匹配上的作废,要退回去,所以不断next.
if(p[i]==p[j+1]) j++;//若匹配接着记录,j表示相同区间长度
ne[i]=j;//存一下
j
作为非平凡前缀的下标
i
作为非平凡后缀的下标
如果非平凡前缀
和非平凡后缀
相同,那就在next[]
数组中存下当前的缀的长度,因为下标从1开始,正好j
作为前缀下标,就是部分匹配值
。
如果在访问新的元素时发现,此时非平凡前缀
与非平凡后缀
不再是相同的字符串,那么就要有j=next[j]
,让前缀退回到上一个位置(详细解释如下):
对于部分匹配值
,即前后缀相似的长度,有
p
[
1
,
n
e
x
t
[
i
]
]
=
p
[
i
−
n
e
x
t
[
i
]
+
1
,
i
]
p[1,next[i]]=p[i-next[i]+1,i]
p[1,next[i]]=p[i−next[i]+1,i]由此可以得知,j=next[j]
就是将后缀退回到前缀的一个操作。在求next数组阶段,可以抽象成是退回到前缀的位置;在模式串匹配的阶段,可以抽象成是把前缀移动到现在后缀所在的位置(针对匹配串操作)。
模式串匹配
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)
cout<<i-j<<" ";//子串的起始点下标
j=ne[j];//直接把前面的匹配成功的点跳过,kmp精髓
//n 匹配串长度
//m 模式串长度
匹配操作在上一部分有所提及,就是不断匹配,在匹配失败的当前下标,直接将满足非平凡后缀
的非平凡前缀
的下一个元素的下标拖到当前位置,i控制模式串,j控制匹配串,也就是将新的j的下标拖到当前i的位置,从匹配串的j位置开始匹配,而不是从头重新开始。
代码模板
#include<bits/stdc++.h>
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;//下标从1开始
for(int i=2,j=0;i<=n;i++)//更新匹配串的next[]数组
while(j&&p[i]!=p[j+1]) j=ne[j];//当前不匹配前面匹配上的作废,要退回去,所以不断next.
if(p[i]==p[j+1]) j++;//若匹配接着记录,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)
cout<<i-j<<" ";//子串的起始点下标
j=ne[j];//直接把前面的匹配成功的点跳过,kmp精髓
return 0;
string版:
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+100;
string p,s;
int n,m;
int ne[N];
int main()
cin>>n>>p>>m>>s;
p="&"+p;
s="&"+s;
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)
cout<<i-j<<" ";
j=ne[j];
return 0;
题解里带过的前缀后缀均指非平凡前缀后缀,有的时候没说全。
参考
补充说明
根据评论区大哥,王道考研规定是 n e x t [ 1 ] ≡ 0 , n e x t [ 2 ] ≡ 1 next[1]≡0,next[2]≡1 next[1]≡0,next[2]≡1如果是期末考试一定要根据教材来,根据定义写next数组,虽然本质思想一样,但是还是要得分。408没考过,架不住自命题可能考,还有期末。。
以上是关于KMP模板的主要内容,如果未能解决你的问题,请参考以下文章