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[inext[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模板的主要内容,如果未能解决你的问题,请参考以下文章

ACM入门之KMP

kmp算法模板及理解

KMP算法模板 求子串和模板串首先匹配的位置

模板KMP字符串匹配KMP

831. KMP字符串(模板)

kmp模板