KMP

Posted et3-tsy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了KMP相关的知识,希望对你有一定的参考价值。

KMP#

问题模式##

给定模式串s1,目标串s2,问s1出现在s2的次数以及位置

期望复杂度:O(n+m)

算法部分##

常规而言,如果之间去扫描目标串并check是否匹配,复杂度就会达到O(n*m)

很自然为了降低复杂度,我们需要更好的方式

比如当aaa匹配成功,我们先不看后面的情况,单论aaa本身:

模式串:aaa

欲匹配的目标串: aaa张三李四王五...

目标串1: aaa张三李四王五...

目标串2: aa张三李四王五...

目标串3: a张三李四王五...

如果aaa能匹配,目标串1中前3个a必能匹配,目标串1中前2个a必能匹配,目标串1中前1个a必能匹配,而目标串2,3是由目标串1后移的结果

所以,如果模式串某个位置能够匹配成功,可能会存在前j个元素在后移过程中也能够完全匹配

我们现在需要记录next[i]=j,他的含义是:

如果模式串中位置i匹配成功,一定存在模式串前j个元素能够完全匹配

(这里位置i是字符串下标,从0开始计数,j是个数,可能为0,注意区分)

也称为位置i之前的字符串中最长相同前后缀的长度为j

例如对于aaabaaaai,有:

模式串:aaabaaaai

对应的:012012330

失配处理:我们如果有了next数组,那我们在i失配的时候就可以一定可以说前j个元素一定是匹配的,现在只需要check模式串第j号下标与当前目标串位置元素的关系,不匹配就继续进行当前失配处理。

KMP的精髓之处在于如何找这个next数组:

其实这个过程运用的就是上面一样的过程,一言以蔽之,“自己匹配自己”

现在只不过将原来的模式串当作现在的目标串来匹配,使用的是一样的过程,

对于位置i而言,next[i]就是他当前能匹配成功的个数

还有一点,匹配成功了怎么办?

判断成功,触发反馈,再当作失配来处理,

这两块匹配过程可以拆开写,也可以合起来写,下面展示的核心代码是合起来的。

核心代码##

s1是目标串,s2是模式串

一定要区分好下标与个数的差别,很容易出错!!

s1=s2+"#"+s1;//连接处理,让两个过程合并,记得加上#,否则一些特例会直接暴毙
next[0]=0;
for(int i=1;i<s1.size();i++)   //这里指的是字符串下标的遍历,从标号1开始
{
	 //模式串中第0号到第c-1号均已经匹配,匹配个数是c个,开始检测第c号下标
	 while(c!=0&&s1[c]!=s1[i])
     c=next[c-1];
	if(s1[c]==s1[i])c++;//if(1)那么说明第c号下标匹配成功,那么有c+1个数已经匹配成功
	 if(c==len&&i>len)       //匹配成功了继续当作失配处理
     {
        cout<<i-len*2+1<<endl;
        c=next[c-1];
     }
     next[i]=c;					//c指的是当前匹配成功的个数
 }

洛谷P3375模版##

#include<iostream>
#include<cstring>
#include<iostream>
#include<string>
using namespace std;
#define INF 1e10+5
#define maxn 10005
#define minn -105
#define ld long double;
#define uint unsigned int;
#define ull unsigned long long;
typedef long long ll;
int main()
{
    string s1,s2;
    int c;
    int next[maxn<<1];
    cin>>s1;
    cin>>s2;
    int len=s2.size();
    s1=s2+"#"+s1;
    next[0]=0;
    for(int i=1;i<s1.size();i++)
    {
        while(c!=0&&s1[c]!=s1[i])
            c=next[c-1];
        if(s1[c]==s1[i])c++;
        if(c==len&&i>len)
        {
            cout<<i-len*2+1<<endl;
	            c=next[c-1];
        }
        next[i]=c;
    }
    for(int i=0;i<len;i++)
        cout<<next[i]<<" ";
    return 0;
}

CF Global Round 7 D2##

核心问题是求前缀最长回文串

注意所求内容是具有前缀性的,所以它模式串是确定的,只不过模式串的长度不定

而将回文串逆置,则根据回文串的性质,这个就是目标串

AC:https://www.luogu.com.cn/record/31985959

以上是关于KMP的主要内容,如果未能解决你的问题,请参考以下文章

kmp算法的个人理解

Python ---- KMP(博文推荐+代码)

KMP算法及Python代码

KMP算法及Python代码

Kmp算法Java代码实现

数据结构—串KMP模式匹配算法