背景

字符串模式匹配,普通模式非常好理解,拿着模式串依次与主串做比较,知道完全匹配,但是这种算法,主串得不断地回溯,时间复杂度O(n*m)。

唐纳德·克努特

有没有降低时间复杂度的可能,唐纳德·克努特等人想到了一种办法不用使主串不停地回溯,而每次使模式串的某个字符与主串的待比较字符对齐,这个算法简称KMP。求解模式串的哪个字符该与这次比较的主串字符对齐,是KMP算法的核心,简称next函数或失配函数。这种算法求解复杂度降低到O(n+m)。

next函数语义

next[j]=k表达的意思是从模式串的 1~j-1 组成的子模式串,最长相同的前、后缀的长度为 k-1。举例说明,如下的字符串,next[6]=3,因为编号为6的字符c的最长前缀为编号为1的a ,编号为2的b 字符,最长后缀为编号为4的字符,编号为5的字符b,所以 k=3。

1 2 3 4 5 6 7 8
a b a a b c a c

再看一下失配函数next[j]的严格定义,模式串字符的编码从1开始。

next函数分析

next 函数值仅取决于模式串本身而与相匹配的主串无关。从next函数的定义出发用递推的方法求next函数值。

由定义得知 next[1]=0,设next[j]=k,这表明在模式串中存在下列关系

"P1...Pk-1" = "Pj-k+1...Pj-1"

图形化显示(一条竖线表示一个字符):

其中k为满足1 < k < j的某个值,并且不能存在k’ > k满足上个等式。此时 next[j+1]=? 分两种情况讨论,

1)若 Pk = Pj ,则 next[j+1] = next[j] + 1 ,即 k + 1 ,如下图显示:

2)若Pk不等于Pj,如下图所示,我们把如下字符,看成一个字符串,寻找它的最长相同的前、后缀:

"P1...Pj+1"

此时我们已知一个条件:

"P1...Pk-1" = "Pj-k+1...Pj-1"

也就是在上图中2个黄色区域表示的前、后缀字符串相等,这样我们依然在上图中的左侧黄色部分中寻找。最终找到了2块咖啡色区域 1~k’-1, k-k’+1~k-1 相等,根据next函数的定义,便是:

next[k]=k'

并且我们根据已知条件 ‘P1…Pk-1’ = ‘Pj-k+1…Pj-1’,可以推导出在右侧黄色区域也存在这样的咖啡色区域,根据等式传递,我们可以得出:

"P1...Pk'-1" = "Pj-k'+1...Pj-1"

因为Pj不等于Pk,所以我们新找出了一个k’(很显然1 < k’ < k),如果它真的满足了 Pj=Pk’,则 next[j+1] = k’ + 1 ,即 :

next[j+1] = next[k] + 1  

如果它很遗憾地又不等于Pj,也没关系,我们继续在[1,k’]这个区间内找这样的K点,如果真的不存在这样的k’,那么 根据定义可以得出:

next[j+1]=1

失配函数代码实现

        /// <summary>/// 失配函数/// </summary>/// <param name="p">模式字符串(编码从索引位置1开始)</param>/// <returns>模式字符串中每个字符的失配值数组</returns>private static int[] getNext(char[] p){int[] next = new int[p.Length];next[1] = 0;int j = 1;int k = 0;while (j < p.Length - 1){if (k == 0 || p[j] == p[k]){next[++j] = ++k; //上述分析中的k'+1赋值给next[j+1]}else{k = next[k]; //next[k]赋值给k,相当于上述分析中的k'}}return next;}

模拟分析

模拟失配函数求解的整个过程代码。

        static void Main(string[] args){string pattern = "abaabcac";char[] pcharsfrom1 = preOperate(pattern);Console.WriteLine();int[] next = getNextWithTest(pcharsfrom1);printf(next);Console.ReadLine();}

预处理字符串,将字符串整体后移1位

        /// <summary>/// 预处理字符串,将字符串整体后移1位/// </summary>/// <returns></returns>private static char[] preOperate(string pattern){char[] pchars = pattern.ToCharArray(0, pattern.Length);char[] pcharsfrom1 = new char[pchars.Length + 1];for (int i = pchars.Length; i > 0; i--)pcharsfrom1[i] = pchars[i - 1];return pcharsfrom1;}
        private static int[] getNextWithTest(char[] p){int[] next = new int[p.Length];next[1] = 0;int j = 1;int k = 0;printf(p);while (j < p.Length - 1){if (k != 0)Console.WriteLine("p[{0}]({1}) == p[{2}]({3})??", j, p[j], k, p[k]);if (k == 0 || p[j] == p[k]){if (k == 0){++j;++k;next[j] = k;Console.WriteLine("根据k=0得出:p[{0}]={1}", j, k);Console.ForegroundColor = ConsoleColor.DarkGreen;Console.WriteLine("--------------------------------");Console.ForegroundColor = ConsoleColor.White;}else{++j;++k;next[j] = k;Console.WriteLine("根据p[j] == p[k]得出:p[{0}]={1}", j, k);Console.ForegroundColor = ConsoleColor.DarkGreen;Console.WriteLine("--------------------------------");Console.ForegroundColor = ConsoleColor.White;}}else{k = next[k];}}return next;}private static void printf<T>(T[] p){int eachlineCount = 10;for (int line = 0; line < p.Length / eachlineCount + 1; line++){for (int i = 0; i < eachlineCount && line * eachlineCount + i < p.Length; i++){Console.Write("  {0}  ", line * eachlineCount + i);}Console.Write("\n");for (int i = 0; i < eachlineCount && line * eachlineCount + i < p.Length; i++){if (line == 0)Console.Write("  {0}  ", p[line * eachlineCount + i]);else{Console.Write("   {0}  ", p[line * eachlineCount + i]);}}Console.Write("\n\n");}}

模拟结果展示:

源码下载:

http://download.csdn.net/detail/daigualu/9791023

算法-发明KMP算法的唐纳德·克努特是怎么想到失配函数next[j]的?相关推荐

  1. 1974年图灵奖--唐纳德·克努特简介

    大家好,我是执念斩长河.今天讲述的是1974年图灵奖获得者唐纳德·克努特.图灵奖奖励他写出百万字的多卷本<计算机程序设计的艺术>.看完本篇博文大家可以获得: 克努特天才的学习经历 多卷本的 ...

  2. 计算机传奇人物之唐纳德·克努特

    唐纳德·克努特(Donald Ervin Knuth)--经典巨著<计算机程序设计的艺术>的年轻作者. 洋洋数百万言的多卷本<计算机程序设计的艺术>(The Art of Co ...

  3. 图解算法:KMP算法

    目录 第一章 暴力匹配实现 第二章 KMP算法介绍 第三章 KMP算法原理 第四章 KMP的匹配表 第五章 KMP算法实现 项目地址:https://gitee.com/caochenlei/algo ...

  4. 数据结构与算法之KMP算法

    数据结构与算法之KMP算法 目录 KMP算法介绍 输入字符串str1,str2,返回字符串str2是否在str1中,在的话在第几位开始 1. KMP算法介绍 在CSDN上看到一篇写的很好的关于KMP的 ...

  5. 【数据结构】字符串 模式匹配算法的理解与实现 Brute Force算法(BF算法)与KMP算法 (C与C++分别实现)

    #笔记整理 若不了解串的定义,可至: 串(string)的定义与表示 查看 串的模式匹配算法 求子串位置的定位函数 Index(S, P, pos) 求子串的定位操作通常称作串的模式匹配(其中子串P称 ...

  6. 《数据结构》实验报告四:串的模式匹配(BF算法、KMP算法)

    一.实验目的 1.了解串的基本概念. 2.掌握串的模式匹配算法的实现 . 二.实验预习 说明以下概念 1.模式匹配: 串的模式匹配就是子串的定位运算. 设有两个字符串 S 和 T ,S为主串(正文串) ...

  7. 【C语言】算法学习·KMP算法

    KMP算法(全称Knuth-Morris-Pratt字符串查找算法,由三位发明者的姓氏命名)是可以在文本串s中快速查找模式串p的一种算法. 要想知道KMP算法是如何减少字符串查找的时间复杂度的,我们不 ...

  8. BF算法优化-------KMP算法

    百度百科:KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt提出的,因此人们称它为克努特-莫里斯-普拉特操作(简称KMP算法).KMP算法的核心是利用 ...

  9. BF算法与KMP算法详解

    目录 一.前言 二.BF算法 代码: 三.KMP算法 next数组: 关于为什么要找最长匹配前后缀: 代码: KMP: 代码: 三.代码汇总: 一.前言 说到字符串匹配,就不得不提BF算法和KMP算法 ...

最新文章

  1. MultiPoseNet:人体检测、姿态估计、语义分割一“网”打尽
  2. 五分钟重温斐波那契数列
  3. hibernate异常:not-null property references a null or transient value
  4. ae抠像插件_AE抠像背景残留去除
  5. sublime快捷操作emmet语法
  6. 任正非:华为不会拆分;以色列公司称可解锁所有 iOS 设备;Java 13 要来了! | 极客头条...
  7. arm linux 进程页表,arm-linux内存页表创建
  8. Oracle常规恢复的实验测试
  9. 深度学习自学(十四):人脸检测-出现cudaGetDeviceCount failed
  10. C++11多线程中的detach()、join()、joinable()
  11. Beautiful Sequence
  12. android 修改系统字体后app崩溃,android 应用在切换系统字体大小和语言时,应用崩溃问题...
  13. 开源开放 | OMAHA 联合 OpenKG 发布新冠诊疗图谱数据
  14. 清华大学计算机系保送,号称计算机世界第一的清华,三位金牌保送生全部投奔了美国...
  15. canvas绘制竖排的数字_小程序利用Canvas绘制图片和竖排文字
  16. “衣带渐宽终不悔,为伊消得人憔悴”的赏析
  17. 局域网自建对讲服务器,Windows 局域网语音对讲
  18. [linux命令]查找包含指定内容的文件
  19. 【限时删除】一个惊艳的神器,可全网爬取各种资源......
  20. 黑苹果引导工具 Clover 配置详解及Clover Configurator使用

热门文章

  1. 查询三级商品分类列表
  2. 如何使用命令安装gcc/g++, gdb, vim
  3. .tsv .csv格式文件
  4. 云原生项目正确的数据库选择
  5. 既要勇于坚持,又要敢于抛弃
  6. 云计算-存算一体-EDA-技术杂谈
  7. 【js、vue-print-nb】js 在Chorme中打印页面去掉页眉页脚
  8. wiley latex 模板 设置双栏 调整双栏间距
  9. 钉钉内部企业应用免登录 php js
  10. caffe版本frcnn的 ROC,AUC,PR,AP介绍及python绘制