前面介绍过,字符串搜索一般来说有三种方式,前缀搜索,后缀搜索,子串搜索。KMP使用的是前缀搜索。

假设p的偏移是i,也就是窗口的位置是i,匹配到位置j+1时发现了不匹配。现在的问题是向前移动窗口到什么位置呢?注意要保证不漏掉可能的匹配,而且为了效率,应该尽量的多移动窗口。

先假设窗口移动到了位置k,看看有什么后果,这里i<k≤j+1,大于i好理解,至少移动一个字符嘛,为什么小于等于j+1?因为σ和u[0]是否相等是不知道的,为了不错过可能的匹配,窗口最多移动到j+1。现在看看子串v有什么性质。子串v应该等于t[k…j],否则k这个位置肯定没有匹配,可以继续移动窗口了。而v=t[k…j]意味着v是u的一个边界。应该注意到u可能不止一个边界,为了不遗漏匹配,v应该是最长的边界。

根据这种思想,Morris和Pratt提出:

  1. 对于模式串的每个前缀u,预先计算u的最长边界b(u)。
  2. 设窗口位置为i,当前文本位置为j,u既是p的前缀,同时也是t[1...j]的后缀的最长字符串。显然u=t[i…j],新读入的文本字符为σ=t[j+1],那么按照如下步骤计算新的最长前缀。
    1. 如果σ=p[|u|+1],那么新的最长前缀是up[|u|+1],计算结束,窗口位置不变;
    2. 如果u=ε,那么新的最长前缀是ε,计算结束,窗口移动到i+1;

否则,置u←b(u),窗口移动|u|-|b(u)|,即i←i+|u|-|b(u)|,转到步骤1执行。

  1. 计算出新的u后,比较u和p(实际上是比较长度),如果u=p,那么发现了一个匹配。然后继续读入下一个文本字符σ=t[j+2],继续ii。

Knuth提出了一个改进,事实上,如果σ≠p[|u|+1],则u的任何边界的后继字符若要与σ=t[i+1]匹配,就一定不能等于p[|u|+1]。基于这个事实,在预处理阶段,对于p的每个真前缀u(p=uw, w≠ε),可以预先计算其最长边界v,使得p[|u|+1]≠p[|v|+1](图中α≠β)。这样,如果σ≠p[|u|+1],那么窗口直接从i移动到i+|u|-|v|,然后再从p的|v|+1位置开始比较(σ与β比较)。

那要如何计算p的真前缀u的最长边界v,且p[|u|+1]≠p[|v|+1]?

我们首先假设现在要处理子串u=p[0…i],找到它满足条件的v,并把它的长度放到数组kmpNext中。注意kmpNext[i]表示p[0…i-1]的满足条件的v的长度。kmpNext[0]=-1;

首先找到u的最长边界。现在设p[0…i-1]的最长边界为v,|v|=j。现在看α=p[i]是否和σ=p[j]相同。如果α=σ,则vα是u的最长边界。如果α≠σ,则vα不是u的边界,要重新找最长边界。

那么找哪个子串呢?看下图:

假设w是这个子串,从图中可知,w是v的边界,设|w|=k。其次,β=p[k]≠σ,因为如果β=σ,那么β≠α,也即是wβ不是u的边界。从上面两点可知,w是v的满足条件的边界。因此如果发现α≠σ,下次就比较β=p[k]和σ。如果相同,那么wβ就是u的最长边界,否则继续取w的满足条件的边界进行上述操作。

找到最长边界后(设最长边界为v),比较p[i+1]和p[j+1]。

如果p[i+1]≠p[j+1],则v满足条件,kmpNext[i+1]=j+1;相同的话,v不满足条件,那么kmpNext[i+1] = kmpNext[j+1],这个解释一下,由于v是u的最长边界,那么满足条件的边界w也应该是v的边界(边界的性质),而且w应该是v满足条件的边界,这样p[k]≠p[j+1]=p[i+1],那么w也是u的满足条件的边界,即kmpNext[i+1] = kmpNext[j+1]。

处理完u后,按照上面的算法继续处理up[i+1],注意到此时vp[j]是u的最长边界。

我们可以注意到,预处理的过程和查询的过程很类似,只不过模式p作为文本,而p的前缀作为模式,也就是说,我们使用KMP算法来对p进行预处理。

下面是KMP算法的C语言实现:

 #define XSIZE 1024
/*kmpNext中保存p的每个真前缀u的最长边界v的长度,v要满足p[|u|+1]≠p[|v|+1]*/
void preKmp(char *p, int m, int kmpNext[])
{
int i, j; i = ;
j = kmpNext[] = -;
while (i < m) {
while (j > - && p[i] != p[j])
j = kmpNext[j];
i++;
j++;
if (p[i] == p[j])
kmpNext[i] = kmpNext[j];
else
kmpNext[i] = j;
}
} void KMP(char *p, int m, char *t, int n)
{
int i, j, kmpNext[XSIZE]; /* Preprocessing */
preKmp(p, m, kmpNext); /* Searching */
i = j = ;
while (j < n) {
while (i > - && p[i] != t[j])
i = kmpNext[i];
i++;
j++;
if (i >= m) {
printf("Found match in %d\n", j - i);
i = kmpNext[i];
}
}
}

字符串匹配算法1-KMP的更多相关文章

  1. 数据结构学习之字符串匹配算法(BF||KMP)

    数据结构学习之字符串匹配算法(BF||KMP) 0x1 实验目的 ​ 通过实验深入了解字符串常用的匹配算法(BF暴力匹配.KMP.优化KMP算法)思想. 0x2 实验要求 ​ 编写出BF暴力匹配.KM ...

  2. 字符串匹配算法之 kmp算法 (python版)

    字符串匹配算法之 kmp算法 (python版) 1.什么是KMP算法 KMP是三位大牛:D.E.Knuth.J.H.MorriT和V.R.Pratt同时发现的.其中第一位就是<计算机程序设计艺 ...

  3. 字符串匹配算法之————KMP算法

    上一篇中讲到暴力法字符串匹配算法,但是暴力法明显存在这样一个问题:一次只移动一个字符.但实际上,针对不同的匹配情况,每次移动的间隔可以更大,没有必要每次只是移动一位: 关于KMP算法的描述,推荐一篇博 ...

  4. 字符串匹配算法之kmp算法

    kmp算法是一种效率非常高的字符串匹配算法,是由Knuth,Morris,Pratt共同提出的模式匹配算法,所以简称KMP算法 算法思想 在一个字符串中查找另一个字符串时,会遇到如下图的情况 我们通常 ...

  5. 动画演示Sunday字符串匹配算法——比KMP算法快七倍!极易理解!

    前言 上一篇我用动画的方式向大家详细说明了KMP算法(没看过的同学可以回去看看). 这次我依旧采用动画的方式向大家介绍另一个你用一次就会爱上的字符串匹配算法:Sunday算法,希望能收获你的点赞关注收 ...

  6. 字符串匹配算法(三)-KMP算法

    今天我们来聊一下字符串匹配算法里最著名的算法-KMP算法,KMP算法的全称是 Knuth Morris Pratt 算法,是根据三位作者(D.E.Knuth,J.H.Morris 和 V.R.Prat ...

  7. Python 细聊从暴力(BF)字符串匹配算法到 KMP 算法之间的精妙变化

    1. 字符串匹配算法 所谓字符串匹配算法,简单地说就是在一个目标字符串中查找是否存在另一个模式字符串.如在字符串 "ABCDEFG" 中查找是否存在 "EF" ...

  8. 字符串KMP——用途广泛的字符串匹配算法 + 扩展KMP——特殊定义的字符串匹配

    引 入 引入 引入 " SY 和 WYX 在看毛片.(几 毛 钱买到的动作 片,毛 片) WYX 突然想回味一个片段,但是只记得台词里面有一句挺长的 " ∗ ∗ ∗ ∗ **** ...

  9. 字符串匹配算法(KMP)

    字符串匹配运用很广泛,举个简单例子,我们每天登QQ时输入账号和密码,大家有没有想过账号和密码是怎样匹配的呢?登录需要多长时间和匹配算法的效率有直接的关系. 首先理解一下前缀和后缀的概念: 给出一个问题 ...

  10. 字符串匹配算法之KMP&Boyer-Moore

    KMP&Boyer-Moore KMP算法是通过分析子串,预先计算每个位置发生不匹配的时候所需移动的下一个位置,直到达到字符串的末尾.KMP&Boyer-Moore算法是通过" ...

随机推荐

  1. Pig Latin儿童黑话(java)

     ●假设单词以辅音字母開始,将词首的辅音字母字符串(第一个元音字母前的全部字母)从单词的开头移动到末尾,然后加上后缀ay,这样就形成了它的pig  latin. ●假设单词以元音字母開始,仅仅须要 ...

  2. _视图控制对象生命周期-init、viewDidLoad、viewWillAppear、viewDidAppear、viewWillDisappear等的区别及用途

    iOS视图控制对象生命周期-init.viewDidLoad.viewWillAppear.viewDidAppear.viewWillDisappear.viewDidDisappear的区别及用途 ...

  3. k8s 集群基本概念

    一.概述: kubernetes是google开源的容器集群管理系统,提供应用部署.维护.扩展机制等功能,利用kubernetes能方便管理跨集群运行容器化的应用,简称:k8s(k与s之间有8个字母) ...

  4. pushViewController自定义动画

    实现的主要代码如下: CATransition *transition = [CATransition animation]; transition.duration = 1.0f; transiti ...

  5. poj2175

    鸣谢: http://www.cppblog.com/y346491470/articles/152317.html [题意]:一个城市有n座建筑物,每个建筑物里面有一些人,为了在战争爆发时这些人都可 ...

  6. ExtJS 4.2学习(一)——环境搭建

    1.把JDK集成到myeclipse里: 虽然myeclipse自带了jdk,但我们还是配置自己的JDK1.7 window——>Proferences——>Java——>Compl ...

  7. Unity 4.2.0 官方最新破解版(Unity3D 最新破解版,3D游戏开发工具和游戏引擎套件)

    Unity是一款跨平台的游戏开发工具,从一开始就被设计成易于使用的产品.作为一个完全集成的专业级应用,Unity还包含了价值数百万美元的功能强大的游戏引擎.Unity作为一个游戏开发工具,它的设计主旨 ...

  8. IronPython初体验和实战集合等类型转换和类型匹配

    人老了,做什么都累~学到了这么多技术,从最早C到C++再到JAVA再到C#,最终都是为了改善产品,改善系统的可维护性.众所周知C#,C++,C都是强类型语言,什么情况都得定义一些实体类来改变业务模型, ...

  9. Python3.5创建虚拟环境

    为每个程序单独创建虚拟环境可以保证程序只能访问虚拟环境中的包,保持全局解释器的干净整洁,使其只作为创建(更多)虚拟环境的源. windows下创建虚拟环境 Python3.5自带venv,只需执行py ...

  10. Oracle字符集转换

            这几天在工作中碰到一个字符乱码的问题,发现在cmd窗口的sqlplus中直接update一个中文和使用@调用一个文件作同样更新的时候,存储的结果 竟不一样.一时比较迷惑,对Oracle ...