浅谈KMP

给你一个文本串和一个模式串,问在文本串中模式串在什么时候出现过。

显然存在一种暴力写法(万能暴力):

从文本串和模式串的开头进行匹配,直到失配,则从模式串开头进行重新匹配。 显然这种写法是很慢的,失配后它只能一格一格地从头开始找。

看下面的例子:

当匹配到以下情况:

那么按照我们的暴力写法,应该是这个样:

它显然是匹配不上的嘛,只能一步步接着跑了,我们当然希望程序能聪明一点.

我们显然是希望程序避免一些无用功,最好能跳到一个最有可能匹配成功的位置,比如:

这不就比之前笨笨的暴力写法强多了吗?

但这仿佛是不可能的,谁能直接告诉程序应该跳到哪里呢?

其实这个算法早有人想了出来(%%%),这就是传说中的: Knuth–Morris–Pratt algorithm(克努斯——莫里斯——普拉特算法)

也就是KMP算法……

它的核心思路就是能告诉程序失配后该从哪里重新匹配。

接下来正式讲一下KMP吧。

观察我们要跳多远:

--->

我们发现,如果设从k开始失配,那么我们可以保证在k之前都是一一对应的。

在这个例子中我们发现结尾(匹配上的)有个AB,开头也有个AB。

AB和AB是一样的,而结尾的AB与失配前文本串中匹配,所以开头的AB一定也是匹配的。

我们考虑将模式串当前位置移到2(从0开始),那就有可能匹配上了。

 

我们总结经验:失配前如果模式串开头和结尾相同,那我们就可以跳到重复子串的下一位,而不是从头匹配了。

所以我们设k失配后应移动到next[k],则next[k]等价于在k之前(不含k)的子串中前缀和后缀相同部分的长度。

如之前的例子就是开头结尾均有个AB长度为2,所以next[k]=2; k就应跳回位置2重新匹配。

next数组是KMP的精髓,许多人之所以不懂KMP就是看不懂next,

这里希望大家能结合例子搞懂next的含义。

 (良心题干)

实现:

第一步---求next: 就是找模式串中前缀后缀相同的长度。

当然我们可以用现成的next来找接下来的next而不一定暴力找。

这代码就是自己和自己匹配,k找的是之前的匹配长度,如果这两位相同,显然当前前缀后缀的匹配长度就是k+1.否则就要从头匹配了(k初值为0)

   cin>>s1>>s2;
    len1=s1.size();
    len2=s2.size();
    for(int i=1;i<len2;i++)
    {
        while(k&&s2[i]!=s2[k])k=next[k];
        if(s2[i]==s2[k])k++;
        next[i+1]=k;
    }

第二步,匹配:

之前求next嘛,你要保证i比k靠后,所以从1开始匹配,现在就是逐位匹配了。

为啥输出i-len2+2 ?

首先我们知道第i位和第k位是匹配位置结尾,所以由:

首项=(末项-项数)*公差+1 可得初始位置为i-len2+1,而因为是从0开始计数,所以再加1.

  for(int i=0;i<len1;i++)
    {
       while(k&&s1[i]!=s2[k])k=next[k];
       if(s1[i]==s2[k])k++;
       if(k==len2)printf("%d\n",i-len2+2);
    }

板子题

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
using namespace std;
string s1,s2;
int k,next[1000010],len1,len2;
int main()
{
    cin>>s1>>s2;
    len1=s1.size();
    len2=s2.size();
    for(int i=1;i<len2;i++)
    {
        while(k&&s2[i]!=s2[k])k=next[k];
        if(s2[i]==s2[k])k++;
        next[i+1]=k;
    }
    k=0;
    for(int i=0;i<len1;i++)
    {
       while(k&&s1[i]!=s2[k])k=next[k];
       if(s1[i]==s2[k])k++;
       if(k==len2)printf("%d\n",i-len2+2);
    }
    for(int i=1;i<=len2;i++)printf("%d ",next[i]);
}

总结:

1.KMP的关键在于next数组,理解KMP的关键就是处理和应用next

2.KMP的思路也可以用来做别的事情,比如找前缀后缀重合长度。

3.KMP考试应用并不多,基本被人遗忘,QYF大佬有立下NOIP考KMP就吃*的flag,也是因为这一点。

引言:

现在哪有人用KMP做字符串匹配啊,顶多在next上跑DP什么的。

                        —— rqy

没办法,KMP就是这么冷门,但是又引申出了它的扩展算法及改进算法(毕竟KMP是个不错的模型) 至于这些嘛......且听下回分解。

 

posted @ 2018-05-05 21:03  _ZZH  阅读(266)  评论(0编辑  收藏  举报